Spring Framework: Why I prefer a simpler solution nowadays

Hydra from DnD, by Wizards of the Coast Once upon a time, the Spring Framework provided a much more lightweight and flexible solution than J2EE. Even in around 2013 I was happy to learn in detail about the then new Spring 4. Nowadays, 7 years later, when I see Spring, I get a panic attack. Annotations and @ComponentScan have replaced XML with something nicer - that requires a visualization tool to understand your system. And Spring has become a hydra that keeps on growing (and changing) heads. I have suffered through taking over and trying to understand a Spring application written by others. And, last but not least, Clojure has taught me how simple code can/should be. So what are my main issues with Spring?


Continue reading →

Want more from your frontend framework! Re-thinking web dev experience

An extended transcript of my talk at DevFest Norway 2020 (slides here).

cover slide

Do you also love creating useful (web)apps and get easily frustrated by any friction in the development process? I will compare Redux + REST with a full-stack, component-centric solution based on a graph API (think GraphQL) that I came to love. You might not be able to use the same framework - Fulcro - but you can still look for similar, more developer-friendly solutions that implement some of the same ideas and provide some of the same functionality. We will discuss REST vs. Graph APIs, networking, error handling, and more. (You should have an idea about React, Redux, and GraphQL to gain most out of this.)


Continue reading →

On crafting troubleshooting-friendly responses in web apps

Once again I have wasted 1-2 hours trying to figure out where the damn "403 Forbidden" was coming from. Yet, with a little forethought and a few seconds or minutes of time, hours could have been saved on such cases. This one would have been trivial if this was already in place, instead of the original, spartan response.sendError(403):

response.addHeader("X-Authority", "HomeController")
response.sendError(403, "Access to the app (temporarily) disabled for everybody")

I would know to look into HomeController and that there is no problem with that particular user’s credentials. I could also search the code for that error message.

In total I have certainly spent days hunting for the source of HTTP errors, especially auth-related ones. So I beg you, when you write any error-handling or secondary-flow code, think about the poor person who is going to troubleshoot it and give her - likely your future self - friendly hints instead of ugly, useless 403 Forbidden / 401 Unauthorized / 500 Internal Server Error / …​. Let’s have a look at what you can do to prevent a lot of frustration and wasted time.


Continue reading →

Error handling in Fulcro: 3 approaches

I present three ways of detecting, handling, and showing server-side errors: globally and at the component level.

By default, Fulcro considers only non-200 HTTP status as an error. It is up to you to tell it what is an error and how to handle it.

This is somewhat controversial - as Programming with Pure Optimism in the Fulcro Developers Guide explains:

A server should not throw an exception and trigger a need for error handling unless there is a real, non-recoverable situation.

And, as Tony explained elsewhere (paraphrasing):

Make sure resolvers never throw, and have them return errors as first-class data. Only (detectable) security hacks and (unexpected) bugs should be hard-core errors. Intentional behavior of your server should always return a sensical value for a query, which may in fact simply be something like: “form save failed”. In that case components can query for problems with a real query prop, and each resolver can populate that key with an error if it has one. So, if you want to do component-level error handling, just adopt that philosophy and make remote-error? assume that something serious went wrong and the user probably should call support, reload the page, and perhaps even log back in. (You can for example define your own defresolver macro that automatically adds error handling.)

In my case, I have an internal application and I encounter mostly bugs and downstream service issues so this approach is a better fit for me than if I had a public-facing application.


Continue reading →

Pains with Terraform (again)

Terraform - the popular infrastructure as code tool and language - looks very appealing at start and many people swear by it. However, I have had a great deal of frustration with it, which I want to share here as an input for future discussions of Terraform vs. AWS Cloud Development Kit (CDK) vs. other solutions. (I have written about this already 2 years ago and there is partial overlap between this and the older Pains with Terraform (perhaps use Sceptre next time?))


Continue reading →

My first month of Rust

Originally published at the Telia Engineering Blog

A month ago I have started learning Rust and would like to share my impressions, the good things I have appreciated, and the things I have struggled with. Why Rust, do you ask? Primarily to challenge myself, to leave the land of managed runtimes (Clojure, JavaScript) and to get as close to the metal as you can without assembly. Knowing a systems (&more) programming language is handy, for example for writing fast serverless functions and command-line utilities. Why not Go? For the same reasons why Clojure: it is more innovative, more mind-bending. Go is optimized, I understand, for approachability (and performance, of course) and is popular for writing web services - but it failed to capture the C/C developers at Google it was aimed at, I hear. Rust's focus is on performance and safety, the latter forcing it to take a really innovative approach to the issue of memory management. And some experienced C/C developers swear by it. So Rust already seemed more attractive to me and reading Bryan Cantrill’s Falling in love with Rust and Sylvain Wallez' Go: the Good, the Bad and the Ugly sealed the deal. From the former:

Rust feels like a distillation of the best work that came before it.

Platforms reflect their values, and I daresay the propagation operator is an embodiment of Rust’s: balancing elegance and expressiveness with robustness and performance.


Continue reading →

Sharing large files on flaky networks with AWS S3 and BitTorrent

How to share a 3GB archive with a remote client when both might have unreliable connection that doesn’t keep up for the full download? Upload it to S3 using multipart upload with a resume capability and download it from S3 using BitTorrent.


Continue reading →

My year 2019 in review

This has been a year of Clojure (and DevOps). Since the beginning of the year, I have worked to persuade my team to adopt Clojure instead of (or rather in addition to) to the current mixture of Java and Groovy, argued to rewrite a key batch job in Clojure (thank you, Monica, for providing the business case!), and did all I could to spread Clojure knowledge and skill in the team and surroundings. It has been a success - the job has been rewritten, we have already benefited from it, and we are finishing a new, Clojure-based micro-service. I have learned a lot about Java vs. Clojure, about the value of business-level tests and "living documentation", about leveraging Spec and property-based testing, about core.async (and error handling). On the DevOps front, I have grown to really dislike Terraform (wishing repeatedly to have a proper programming language instead of the frustrating Terraform DSL and the hacks it requires, not mentioning the nightmare of upgrading providers and Tf itself; 🤞for CDK/cdk-clj), have spent more time then I ever wanted with Kubernetes (and am working hard on simplifying our infrastructure and replacing K8s with AWS Fargate and thus spending less time on operations and more on development), and fell more and more in love with Clojure REPL, whether embdded in a Java app, in an actual Clojure code, or opening the door (securely!) to a "serverless" container on Fargate.


Continue reading →

Error handling in Clojure Core.Async (and its higher-level constructs)

Error handling is something that core.async leaves to you (because it depends on your particular use case). People have written before about handling errors in the low-level go loops but there is little about the higher level constructs such as pipelines and transducers. I’d like to correct that. First, a little repetition.


Continue reading →

Secure REPL access for AWS Fargate services

You are running a Clojure microservice on AWS Fargate in a private VPC subnet and want to have a secure REPL access to it, only exposing the port inside the VPC. Moreover you would prefer to use SSM Session and SSM Port Forwarding instead of running and exposing SSH. Here is how to do it. (Beware: it is not as trivial as we would like.)


Continue reading →

A brief look at the internal structure of Clojure Zippers

Clojure Zippers is a library for navigating and modifying tree data structures. While refactoring Cryogen, I needed an operation not supported out of the box (the removal of all nodes to the right of the current one) and thus had to learn a bit about the internal structure of zippers. I record it here for posterity.


Continue reading →

Migrating my blog from Gatsby to a static site with Cryogen and AsciiDoctor

A year ago I have moved my blog from Wordpress and its WYSIWYG editor to a static site written using React and GaphQL and generated by Gatsby, with entries in Markdown, hosted on Netlify. My motivation was control and ease of writing. Now I continue the trend by moving from Gatsby to Cryogen, a blog-focused static site generator in Clojure, and AsciiDoctor.


Continue reading →

nREPL over HTTP(s) with Drawbridge in 2020

Sometimes the only way to get REPL into your production application is to tunnel it over HTTP. nREPL has a transport and Ring handler for that provided by Drawbridge. Heroku has a nice but too dated guide on using nREPL with Drawbridge. I would like to fill the missing bits here.


Continue reading →

AWS Fargate: Troubleshooting the dreaded '`service .. is unhealthy`'

So you have just deployed your Docker container to AWS Fargate but it keeps on restarting with the event “service XYZ (..) is unhealthy ..” and you have no idea why. I have spent many bloody hours here and will gladly share my insights with you. The 3 key questions to ask are:

  1. Do requests for / return 200 OK?

  2. Do they return quickly enough?

  3. Does the service start responding quickly enough after a start?

(+ a bonus question and more troubleshooting tips)


Continue reading →

How Clojure helped us recover from bad data

On a calm autumn morning we got a desperate call from our customer service. Our biggest customer had just started a pilot of our “expense share” functionality - and was missing half of their data. And they absolutely needed them for sending salaries in a few days. We jumped into production, cross-checked the data against a few of the missing employees and quickly determined that they were missing from the incoming invoices. We were able to cut and glue pieces of production code to extract just the information we needed - counts of employees and invoices past, present, and expected for billing runs in the problematic period - and thus identify all affected customers. After a fix in the source system, we were able to repeat the billing runs without writing any data, just to verify it generated the correct results - and write the results later. Combining data from web services and the database, correlating them and massaging them just into the format needed, interactively, with immediate feedback - that was only possible thanks to the code being in Clojure and thanks to Clojure REPL. Without it, the troubleshooting and correction process would have been much more difficult and time-consuming. Let’s look in detail how exactly did it help.


Continue reading →

Copyright © 2024 Jakub Holý
Powered by Cryogen
Theme by KingMob