All the small things

As I cruised the internet last night I came across this Hacker News thread about rewriting an entire application. At work we've been having talks about rewriting our frontend code. It is written in AngularJS which is awful and reached end of life on December 31, 2021

In the thread a commenter mentions the talk "All The Small Things" by Sandi Metz. I figured I'd do what I normally do: start the talk, get bored 2 minutes in, and then move on to other content. I ended up watching the talk the whole way through and then waking up in the morning and writing this post I enjoyed it so much. Sandi is an excellent speaker and very clearly describes the concepts she's talking about. I highly recommend watching the talk.

Seams

The talk focuses quite a bit on object oriented design. At work we don't use many objects. But, I think the concepts the talk focuses on through objects are concepts we use. These concepts have proven to be life savers when complexity needs to be added to our system (ex dynamic dispatch).

One concept Sandi brings up is “seams”. My interpretation of this is they are places where you can get a handle on what needs to happen, dispatch, and then go to one small area of the code to handle the complexity for just the thing you care about. The opposite is what the code in the talk starts as, complicated branching where all of the complexity for everything is mixed together.

Hourglass

I've come across this concept before and thought of it like an hourglass. For example, at work all functions that begin with def api_ are our API endpoints. In our Flask server (server.py and job_api.py) I think of them as the top of the hourglass. They are varied and do many different things. But if you squint all of these API endpoints share some common functionality. They need security (who can access it?), they have common input (how do we parse the request to convert json to a Python dict?), and they need common resources (is the database initialized?).

At the small constricted neck of the hourglass we answer these questions. Importantly, we answer them abstractly for all API endpoints and we do it in only one place (uri_router.py). I think this is the seam.

The API requests are then dispatched (there is even a _dispatch method in uri_router) to their respective function. At that function a complex but small and isolated bit of code unique to that API happens.

Using seams/hourglass to fix problems

I've put these concepts to use before. We once had an issue where we had a bug in threaded code because we were clobbering flask.g (a thread safe datastructure). Here is the PR that fixed the bug. It is pretty hairy but we fixed many concepts along the way. The important part of that PR with regards to seams/hourglass is that we were able to add with sirepo.auth.process_request() to the _dispatch of uri_router to fix all APIs. process_request is tricky code. It handles threaded (flask) and non-threaed (tornado) environments and sets up our auth (cookie) and database (sqlite) for every request. By having all API requests go through _dispatch we had a handle (a seam or the neck of the hourglass) where we could do this.

Had we not had this seam I think the fix would have been harder to identify. Answering a question like "how do I do this for all APIs?" would have been overwhelming. Likewise, it may have lead to a solution like adding a new decorator to every def api_* which spreads more complexity throughout the codebase and introduces more areas for error (ex. forgetting to add the decorator to a new API).

How to write code with seams

Sadly, I'm going to leave you empty in one critical way as the talk left me. How does one identify code without seams, find what the seams are, and add them? I think I'm ok at adding seams. Maybe I can identify code without them (lot's of complicated logic mixed together). But, I'm not confident I know how to find what the seems are. For example, I could use uri_router to add complexity (using the seams) but I didn't write that code initially.

My hope is that by having this new mental model, "seams", maybe I'll be able to add them in the future. If you know of a good way to identify what the seams should be in an application please reach out.