Earlier this year I wrote an outspoken post following the online protests when the young author Marieke Lucas Rijneveld was commissioned to translate Amanda Gorman’s poem The Hill We Climb into Dutch. Rather than pointing out their non-existing experience as a literary translator, the protest centered around their clear lack of epidermal pigmentation. The job should have been given to a person of color. Rijneveld returned the assignment with the usual undignified and probably disingenuous apologies for having wounded certain sensitivities. I side with Ricky Gervais on this one: just because you’re offended doesn’t mean you’re right.
You may think this is the typical stance of a white Gen-X male, university educated and raised in an affluent, traditional rural Dutch milieu. And you would be spot on. My background colors everything I do and opine, including the previous paragraph, the next ones and everything I have written and will write on this blog. I could not be neutral if I wanted to.
All software teams I have worked in over the last ten years practiced some sort of Agile, sometimes effectively, sometimes in-name-only. I discovered a lot about the value of teamwork and leadership and I increasingly side with the Agile2 movement that the original ideas expressed in the Agile manifesto on these topics are routinely misinterpreted and misapplied. Software teams are not like sports teams. Not a bit.
In my previous post I explained why we should write more unit tests for our database code. Databases contain crucial business logic that is often developed and maintained in tandem with the code that depends on it. If we want to validate complex SQL statements in isolation and in detail, that means writing much more automatic tests than we normally do. Testing against a real database server (not an in-memory emulation) is by nature slower and more cumbersome to set up and run than basic unit tests. Here I will explain some practical strategies to make that process faster and more manageable.
The tips and tricks fall into two categories. First, I’ll look at ways to arrange your code under test so that the database is only spun up and accessed when it is actually needed. Secondly there are tips on how to prepare your database container with schemas and data, so setup time is kept to a minimum.
The test pyramid is a well-known visualization aid to classify software tests. As you climb the steps towards greater integration of components, you proceed from many, detailed, fast and isolated tests towards fewer, slower and more global tests that validate the system as a whole. It makes good sense in principle but it’s harder to explain how the stratification between unit-, integration and end-to-end tests is supposed to work. Opinions differ on what parts to integrate at which layer in the pyramid. You would think the database belongs in the upper strata, because it is expensive to set up and run. Yet it also makes sense to integrate it at the lower, detailed stage in the pyramid when it contains business-critical logic that requires detailed validation. It often needs the kind of rigorous validation that you cannot leave to a few slow, global integration tests. But let’s start with a recap of the definition of a unit test.
Summary: It’s deceptively simple to enable caching in Spring, but you must investigate actual production usage in order to configure it properly. There are four areas of concern: peak-time load, uniqueness of requests, savings in time and resources, and the longevity of cacheable values. An example project shows these concerns in action.
I like the topic of caching in Java code. It’s a technique where the Platonic world of clean code meets actual usage. You can never tell just by looking at code whether caching is a good idea or unnecessary optimization. You need to measure or at least predict realistic production loads.
Maybe the Spring framework had made it a little too easy. With minimal boilerplate you can configure any managed object to return cached responses upon repeated requests. Call it once to run the method body, call it twice and the framework intervenes and returns the result of the first call. You can (well, must) plug in a full featured third-party implementation like Caffeine or ehcache to enable things like disk overflow and automatic eviction.