Keep It Simple, Seriously: Building a no-frills Secret Santa assigner

3 min read

Author's note: Yes, I haven't posted in a year. I published my MSc thesis, and have started work with Amazon Web Services. But this post is quick and fun. And I'll be memeing Amazon's Leadership Principles™️ along the way, as a homage.


Okay, let's get started and set the scene: every year, my family gets together for Chrismas eve, and a tradition that we have, since we are so many, is to do a Secret Santa - everyone, kids excluded, draws another member of the extended family to gift to; then, instead of having to gift everyone a bunch of useless trinkets, they can give a better thought out present! Seems simple enough ☺️.

Having started working, and so being an "adult" for the first time this year, I was excited to take part in this tradition, but I was dismayed to learn that it was being threatened by a death by a thousand cuts. With so many people now participating, it was getting unwieldy to draw names for everyone, since it needed to be done, by hand, on an ocasion where at least one person of every household of the family was present. Plus:

  • The people that usually do the drawing, usually my beautiful mother or one my gracious aunts, often needed to be the 'gamemaster' and know some of the assignments to control for restrictions in the assignment.
  • Having many participants means that some of them ended not being present on the actual Christmas eve for the unwrapping, cheapening the experience a bit. And that the thing itself took too much time, which made the kids cranky.

Okay, so let's work backwards™️. The main papercuts are:

  • Not everyone that usually participated would be present.
  • It's hard to make a draw in person.
  • Too much knowledge on one person about the assignments.

The first point was the harder to solve, on human terms. We are making the difficult decision of only allowing the ones present on Dec 24 to participate. This will allow the experience to be more focused on the ones present there.

Now to the second and third ones. We need automation to solve this. And, to totally, definitelly honor the invent and simplify™️ motto, we're not gonna use one of the billion existing apps. So I started prototyping.

Now, a thing you should know about me is that I sometimes obsess over little things and get stuck in the analysis paralysis stage of design. So, for building my secret santa assigner, I started thinking of several layers to automate more than one draft, how to approach this (basically NP-complete) problem in the most efficient way, and so on, and so forth.

And so I was not making any real progress.

Until it hit me: Keep It Simple, Seriously!

I made an experiment - I got my trusty Deno environment up, and quickly modelled the problem. You have a bunch of names, and a set of restrictions (in our case, you never are the secret santa to anyone in your household). So the input was something like this, at the bare minimum level of detail:

type SecretSantaInput = {
  people: Set<string>;
  pools: Set<Set<string>>;
};

I put some representative input in a JSON file, imported it, and made the simplest algorithm I could think of - just making random candidate assingments until one fit the restrictions. To my surprise, it always worked in a fraction of a second, in about 10 iterations.

Which means that in a couple more hours, I added several more features:

  • Use Amazon SES to send emails to everyone with their assignment. Frugality™️ cause it means I can use my personal account's Free Tier allowance and I don't need to figure out how to send SMTP emails via my Gmail account.
  • Pool together messages to the same email account, cause older folks will not know how to check their email.
  • Validate input with zod to make sure I don't screw up the input.

And it's done! No frills, exceedingly customer-obsessed™️, and makes my computer nerd happy knowing that I made it myself.

Now I just need to start a business from (in some way that beats all the free versions available and doesn't conflict with my non-compete clauses 🙃). The first business decision I will make is to give away the code for free.

Happy holidays, everyone!

Continue reading →

Who gives a sh*t about the tech, just write something; or how I finally started my programming blog

5 min read

I didn't think my second blog ever would already be a meta post about blogging, but, facing a lack of an actual topic related to tech, because consistency makes the habit, and because I did not want to skip posting this week, here I am, writing about writing. I'll keep it as short as my beginner writing skills allow me to, I promise.

My close friends might know this, and everyone else might not, but could have guessed it: I had been thinking of starting a blog for almost two years.

The reasons for wanting to start blogging were utterly unremarkable:

All the cool kids on the developer web do it

The web is full of really interesting blogs that let you:

  1. Get cool content and perspectives on all kind of topics related to the industry. It makes you think that starting a blog will let you share your opinion and connect you to both like-minded people and reasonable discussion.
  2. Find informal tutorials and guides for topics that are a bit out of the ordinary: by focusing on a very specific use case, and unlike reference documentation, they often touched on nuances and on the unexpected issues of a certain solution. I also wanted to share the cool quirks of the technical problems and solutions I encountered.
  3. Had their own flare of personal brand or personality: the relationship is entirely parasocial, but you feel a connection to the author's personality.

Even without these feelings being mixed in, I also noticed that I often knew names of people that wrote often about certain topics, and associated them to a degree of expertise. This aspect leads us to the next reason:

It's a content marketing strategy

Another reason I wanted to start the blog was a more practical one, which was to start putting my name out there as a professional in the industry. The reality of our world is that we will need to sell something to survive, so we'd better plan out how we want to market ourselves. I feel the need to cultivate an image of competence (hopefully reflecting the reality of being competent) to make sure that if work for others they will see me as worthwhile for the team and deserving of compensation, or, if I ever ever start a business, being able to project my personal branding into an image of quality for the product. Blogging made sense to me as the more straightforward way to put out content to build up that branding.

I wanted a coherent, shareable, durable, copy of my thoughts

One thing that happens often to me is that I think of something, I want to tell someone about it, and I get bogged down communicating it. Often it happens because I did not realize I had taken some mental shortcut to ground my conclusion, had disconnected thoughts that contradicted each other, or did not know how to organize those thoughts. Writing things down then helps me think through them with rigour and be more coherent.

The other thing that happens was having the same conversation over and over with different people, repeating myself, or worse, making slightly different points. I wanted a shareable record of those thoughts I had, so a notebook was not enough. I needed something that I could message to someone and say "read this" (such as website!).

Finally, I wanted to make sure that those thoughts were easily found and were durable. This ruled out just making LinkedIn posts, since they would have poor discoverability and searchability, and a blog seemed the best option for that.

Which brings us to the roadblock ahead:

Not-in-House Syndrome: The #1 Dev Blog Killer

This is the thing that blocked me for two years: wanting to blog for the sake of blogging, and thinking "I have to make my own personal everything". It starts with the easy things: get a domain, pick a name. Then eveything breaks down. How far do I want to customize my blog? Do I use a custom theme for a static site generator? Do I need a content management system? Should I write my own logic for routing? Do I deploy on Netlify with Gatsby? Do I run Ruby on Rails on Heroku? How do I want it to look.

To make myself clear: if you want to implement you blog without writing, you will get bogged down in the minutiae. This is especially true if you can't design for sh*t, like us backend folks, and/or if you do not even have content yet.

The book Atomic Habits by James Clear, regardless of your opinion on its methods, validity, et cetera, got at the very least one thing right: you will not pick up a new habit if you do not make it easy. And this insight was what made me finally able to start a blog.

Previously, I had tried using half a dozen different frameworks, went as deep down as to wanting to just write it as simple HTML files and a CSS sheet, and the same thing always happened: I had no content, and so 1. I could not ever publish it and 2. I lost interest in it within days.

Last week, I was reading a bit, I had just implemented a pattern that I thought was cool in a codebase's mock data generators, and thought to myself "I could just write about it". And so I did. Finally, my first blog post ever went online.

Which brings me to my solution to this problem:

Just write something

If you want to start your blog, you just need content. Literally everything else comes naturally afterwards, if your goal is just to get some text online. Seriously, if you have an idea for a blog, just write the text you want to share down, even if it takes 1h30 to write, like this one is.

The moment you have a text, be it in your notes app, on a Google Doc, or as a markdown file, everything afterwards becomes much clearer.

If you just want to share it, you can just paste it in a social media platform, on Medium, or literally anywhere else.

If you want to control your site like I did, but everything else seems too complicated, just get a SSG and the simplest theme possible, like I did with this blog, Hugo and PaperMod, and publish it ASAP. If you feel the need to customize later, you will have the urge to do it and can do it knowing that at least your stuff is already online.

If you still want to have everything yourself, you will finally have a reason to actually do it. The content is there, and you will feel the need to finish it and put it online.

Then just keep on writing. The content is what brings people over, not the blog's tech.

Continue reading →

Fun DSLs in Kotlin - Builders and Receiver Types

5 min read

We use DSLs everywhere. Every language has different patterns, so today I wanted to share a fun Kotlin pattern that you can use for builders in your embedded DSL.

A Convoluted Example

Let's say that we are building a scraper today to help us scrape all the passwords off an unprotected admin page in a website. The admins have access to a lot of JSON endpoints that look something like:

{
  // ...
  "passwords": [
    {
      "username": "john.doe",
      "password": "hunter2"
    }
    // ...
  ]
}

And ideally we wanted to scrape a whole lot of files and URLs fast, and so we came up with the following implementation:

data class Credentials(
    val username: String,
    val password: String,
)

class JsonScraper(
    private val query: String, // a query string like ".foo.bar.passwords"
    private val urls: List<String>,
    private val files: List<String>,
) {
    fun scrape() : List<Credentials> {
        // this is magically implemented to scrape everything
        return files.map { scrapeFile(it) } + urls.map { scrapeUrls(it) }
    }
}

However, actually writing a quick script or program using this API is not as fun as we had hoped. After all, we need to get the files and URLs, build the lists on our own, and pass them into the constructor like this:

val scraper = JsonScraper(
    query = "...",
    urls = listOf(url1, url2, ..., urlN),
    files = listOf(file1, file2, ..., fileN),
)

For small, relatively homogeneous classes without big constructors this might be good enough (and in those cases: great! YAGNI), but in some others we might want a more straightforward way of managing complex object constructors.

In comes the Builder

Note: if you are familiar with the Builder pattern you can skip this section

I am not the biggest fan of design patterns, but a relatively simple one that can be used often is the Builder pattern. If we go and consult a reference page about it, for example this one, we can see that the intent and problem statements pretty much match our own.

We introduce a new object, called a builder, that lets us build our more complex object step by step. Let's try to implement it in the most traditional, Java-y way first.

class Builder(
    private val query: String
) {
    private val urls = mutableListOf<String>()
    private val files = mutableListOf<String>()

    fun withUrl(url: String) : Builder {
        urls.add(url)
        return this
    }

    fun withFile(file: File) : Builder {
        files.add(file)
        return this
    }

    fun build() = JsonScraper(query, urls, files)
}

So what is the advantage of this kind of pattern? We can, for example, take only the core parameters in the constructor and safely build up the optional components afterwards. So, in a case where we are only adding files, instead of having to explicitly set and empty list like:

val scraper = JsonScraper(
    query = query,
    files = listOf(...),
    urls = emptyList(),
)

We can just ignore the URL option all together:

val scraper = Builder(query)
    .withFile(file1)
    .withFile(file2)
    //...
    .build()

This construction is also more incremental in nature, so we can apply it in cases where we receive a stream of options (urls.forEach { builder.withUrl(it) }) or when we are receiving commands from a terminal UI.

Improving the Pattern with Lambdas

This can already work well in many cases, but there is a use case where this pattern might not work well enough as is.

Let's imagine that we have a smart part of our code that preconfigures the builder with certain parameters, but afterwards still wants to yield back the control to the user to set certain parameters manually. In that case, we end up in the awkward situation of having a half-done builder sent into our code, which we have to finish, build, and send back into the code. Something like:

val builder = AiAssistedScraper.configBuilder() // already presets parameters
    .manualAdjustmentX()
    .manualAdjustmentY()
val scraperConfig = builder.build()
val scraper = AiAssitedScraper(config)

This involves a lot of boilerplate code on the user's end. Luckily, we can use first-class functions to simplify this API. What if we just pass in a function that modifies the builder at will, and the second-order function takes care of the instantiation, preconfiguring and building for us?

fun AiAssistedScraper.buildScraper(
    manualAdjustments: (ConfigBuilder) -> Unit
) : AiAssitedScraper {
    val builder = ConfigBuilder(/* preset parameters */)
    manualAdjustments.invoke(builder)
    return scraper(builder.build())
}

Now the user only needs to focus on the manual adjustments:

val scraper = AiAssistedScraper.buildScraper {
    builder -> builder
        .manualAdjustmentX()
        .manualAdjustmentY()
}

Much simpler!

This pattern is used extensively in the wild. For example, for building database migrations on Ruby on Rails:

# From https://guides.rubyonrails.org/getting_started.html#database-migrations
class CreateArticles < ActiveRecord::Migration[7.0]
  def change
    create_table :articles do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end
end

Notice how the name of the table is passed into the function as an initial argument, and how afterwards the other parameters are set using an inline function (in Ruby they call them code blocks).

The Cherry on Top: Receiver Types

Kotlin has one more surprise for us: function receiver types. These are more properly called, in the language's parlance, function literals with receiver, and work as follows:

  1. You declare the function as having, e.g., the type A.(X, Y) -> B
  2. You write your lambdas in the client code: they take two arguments of type X and Y, and return type B. Moreover, there is an implicit environmental variable of type A that can be accessed using just a bare function call. So, if A is a string, you will have access to the length field just by just using the unqualified identifier (like a variable).
  3. You call the code in the service with function.invoke(a, x, y) or, like an extension method, with a.function(x,y)

This can make using the API even easier, as we don't need to include an explicit builder parameter in our lambda.

So how does it work in practice? Let's just build on our code from before and write a simple function:

fun buildScraper(query: String, buildAction: Builder.() -> Unit): JsonScraper {
    val builder = Builder(query)
    builder.buildAction()
    return builder.build()
}

// we can now easily build our scraper!
val scraper = buildScraper(".secret.passwords") {
    withFile("./torrentedPasswords.json")
    withUrl("https://example.com/_secret/credentials.json")
    withFile("/mnt/flashdrive/stolen_passowrds.json")
}

And that is it! I hope you find this pattern useful in your code adventures. This is my first blog ever, so if you have any suggestions let me know.

Note: You can use any code sample here, except the one from the Ruby on Rails tutorial, for any purpose and without restrictions whatsoever, provided you agree that I am not liable and there is no warranty whatsoever to any effects it might cause. The RoR sample is a work of its respective authors, and licensed under the CC BY-SA 4.0 license.

Continue reading →

More posts can be found in the archive.