Journaling into an Empty Space

I stumbled upon an environment hack that helps me journal every day. Before this year, I just kept a running journal—each day just followed the last at whatever part of the page where the last one ended. If I skipped a day, then the journal just jumped in time. If I skipped a month (or two), then there was a bigger time jump. It’s annoying when I look over the journal, but there’s not much I can do about it.

Now I use a journal where there’s a space for each day. If I skip a day, I can reconstruct it from memory later. But, because there’s an empty space, I don’t often skip it.

The Third 13 Weeks

I journal in a Recurring Journal I made last year — it splits the year into four 13- week cycles where I journal the same day in the cycle in the quadrants of a two-page spread. We’re now in the in the third cycle—today is August 23rd, which I am journaling in the upper-right. In the upper-left quadrant, I see what I journaled on February 22nd, and in the bottom left quadrant, I see May 24th. Journaling this way gives me a chance to reflect on a similar day about 3 and 6 months ago.

The design of the journal makes sure that all four days on the spread are the same day of the week, which is why I use 13-week cycles instead of calendar quarters. A random Wednesday is August is a lot like a random Wednesday in February. For example, I can see a note from a recurring meeting with a mentee that I also met with today. I can see notes on my long-term project and feel a little joy in the progress I have made.

But, just as I noted in The Second 13 Weeks, the main benefit I am getting from journaling this way is that I rarely skip a day, and if I do, I backfill it. I know that I will be revisiting that day in the future, and it will be missed if it’s not there.

Writing While Writing

A few years ago, I wrote a post, Writing While Reading, about how I write notes in Obsidian while I read.

I also write while I write.

While I am writing a blog post, I often will write whole paragraphs that don’t fit. If I’m doing a good job of editing, I will remove that paragraph, but I don’t delete it. I select and cut the paragraph, but then I go to Obsidian and paste it into a new note. I try to find at least one other note to link it to.

At some point in the future, that paragraph might find itself in a post where it makes sense. Or, more likely, I will add more notes around its core idea and develop something around it.

Tailwind Final Thoughts

I just finished migrating App-o-Mat from Bootstrap to Tailwind. Here are some things I recommend if you are trying something similar

  1. Don’t try to keep the same markup. It’s likely you can make it a lot simpler.
  2. If you need icons, use Heroicons.
  3. If you use @apply, use it to style tags or to make more utilities.
  4. Generally, Tailwind seems mature enough that what you think of as an edge-case is covered somehow.

The next thing I will try is using it in a React site (with TailwindUI components)

Write While True Episode 31: The Order of Words

I’m going to use these three books again for today’s topic, which is the order of words in a sentence.

I love that these books tackle mundane topics, things that seem innate, but aren’t. I mean, we can order words into something grammatical without much thought. Even though a ten-sentence word has ten factorial possible orderings, we can easily find the handful that are legal English.

Transcript

Public Software Playground Projects

I have dozens projects on my drive that are playgrounds for me to try out things I am learning. Each of them is a simple example of a “type” of project (e.g. a React Native app, a React App, a clojure program, a SwiftUI app, etc). I don’t actively maintain them until I need to use them again.

Two of these playground projects are public: Habits and App-o-Mat.

Habits is my first iOS App. I made it in 2008, and it’s the codebase I have continuously worked on the longest. It always builds in the latest Xcode, and it has both Objective-C and pre-CoreData sqlite code. It also has modern Swift.

App-o-Mat is a content site for iOS and watchOS tutorials, which is made in Django with a simple HTML-heavy front-end. I made it in 2014, and it’s my second longest continuously worked on codebase. I regularly migrate it to the lastest Python and Django (it was originally Python 2 and Django 1.0).

Whenever I want to try out new iOS features, I can usually find a place in Habits to do that. When I want to try out a new CSS framework, App-o-Mat is simple enough to try it out and see if I’d like it, but not too simple.

I started converting App-o-Mat to Tailwind a couple of days ago. I have run into enough edge-cases that I am sure that I am giving Tailwind a good look. I have tried out other CSS frameworks in the past (even just trying to update to the lastest Bootstrap) and it hasn’t gone as well as this is going. Tailwind is different enough that you need to try it on something real, and App-o-Mat

Making them public has been an incentive to keep working on them, which means they are playgrounds that are always ready for more play.

Tailwind Seems Nuts Until You Try It

Tailwind makes a complete mess out of your HTML. When you first try to do anything, it seems completely wrong. It feels like just a fancy style attribute.

But maybe all we needed was fancy style attribute after all.

I just finished rewriting the HTML for App-o-Mat‘s home page. I still have a lot to do, but the benefits are obvious:

  1. There is no CSS file (well not one that I write or maintain)
  2. My HTML structure is a lot simpler
  3. My site is in Django, so I have lots of ways to generate HTML partials and components
  4. The default behavior is very responsive and I can easily override what I need to
  5. It is trivial to debug in the browser inspector (the style is in the tag)
  6. They have thought of complex use cases like the typography plugin that can style markdown-generated HTML that you don’t control

Tailwind is the kind of thing that is more obvious if you’ve worked on bigger projects. The arbitrary class names, the disorganization, not knowing which class is bringing in which behavior, the chance that you mess up some other page that is sharing the class—that’s all solved with Tailwind.

The drawback (insane class attributes) is easily solved with the way we normally generate HTML in real projects.

It’s only day two, but it does feel to me like this makes more sense than Material (which is what I have been using for UI in my React projects). It will depend on how good the controls in TailwindUI are and if I can find what I need elsewhere.

My First Few Hours with Tailwind (2023)

I am late to Tailwind, which is how I like it, but Matt Rickard’s post convinced me that Tailwind has “won”, and specifically that it is the heir-apparent to Bootstrap. I’ve been looking to get App-o-Mat off of Bootstrap for a while, so I’m in. Ultimately, I might want to move to Tailwind for all of my projects, but App-o-mat is the simplest, so I’ll start there.

App-o-Mat is a Django site with almost no client-side javascript and a fairly simple design using Bootstrap (not flexbox) for layout. It was made in 2014, and it’s very much of its era.

My first impression was despair and I almost almost gave up right at setup because it needed npm. This made me think that Tailwind was not going to be compatible with a simple, HTML-dominant site.

I googled a bit and found some Django plugins, but they didn’t seem simpler or widely used. I googled a bit harder and eventually found the Tailwind Standalone CLI. This is more like it. It’s easy to adopt and to see exactly what it does, so I still end up with a project and deployment I understand.

The next issue I had is that App-o-Mat does have some forms and navigation where I need some UI Components. So, I ended up at TailwindUI, which I guess I need to buy, because there is no way I want to implement any of these components. I think Tailwind is going to wind up in more of my projects, so this price isn’t that bad.

I started planning out the project and decided that this was also a good time to bring the project into VSCode (from PyCharm). I love PyCharm, but GitHub CoPilot is too important to give up, and I’m so behind in my PyCharm updates, that going to VSCode seems about as easy. All of my other python projects are in VSCode now anyway.

The Most Consequential Clojure I Ever Wrote

In my congratulations to Rich Hickey, I mentioned that clojure influenced my code, but that I never wrote any professionally. I did, however, apply to a job opening at FogCreek/Trello with clojure code. They gated the application process with a programming question.

I call this code the most consequential clojure code I ever wrote because it got me an interview at FogCreek, which ultimately ended up in me working at Trello for almost seven years, during which we were acquired by Atlassian.

Since the question has been publicly answered many times, and is no longer used, I’ll reproduce it here:

Find a string of characters that contains only letters from “acdegilmnoprstuw” such that the hash(the_string) is 910897038977002.

If hash is defined by the following pseudo-code:

hash (s) {
h = 7
letters = "acdegilmnoprstuw"
for(i = 0; i < s.length; i++) {
h = (h * 37 + letters.indexOf(s[i]))
}
return h
}

For example, if we were trying to find the string where hash(the_string) was 680131659347, the answer would be “leepadg”.

I chose to use clojure because I didn’t know how big the value of h was going to get and wanted access to clojure’s implementation of BigInteger, which is seamless. All integer math in clojure promotes up to whatever size integer it needs. Once I thought of using clojure, I realized that it would be seen as a fun choice by the developers reviewing the code since FogCreek had a reputation of hiring developers that were interested in weird programming languages (see wasabi).

If you don’t want any hints on how to solve this, stop here.

So, they are asking you to invert the hash() function. In general, hash functions should not be invertible, but this hash is made by using simple lossless integer arithmetic with * and +, which both have inverses (/ and -). Each step of the function is invertible, so the function itself is invertible.

I decided to start with just implementing their pseudocode in clojure.

(defn fchash [arg]
  (let [letters "acdegilmnoprstuw"]
      (loop [h 7 s arg]
          (if-let [c (first s)]
              (recur (+ (* h 37) (.indexOf letters  (int c))) (rest s))
              h))))

And to make sure this was correct, I evaluated (fchash "leepadg") to make sure I got 680131659347.

There are a few tricks to inverting the code.

First, you need to figure out when the loop is done. You don’t know how long the string is supposed to be, so you need to detect somehow that you are done. For this, note that the hash is originally seeded with the number 7, so we expect that we’ll eventually get back to this number in the inverted version.

Second, you need to figure out what number was added after the hash was multiplied by 7. To get that, you can use mod 37 because the number must be less than the length of the letters string (which is 16) and 16 is less than 37, so using mod gets you the number that was added to the hash after it was multiplied by 37.

That should be enough to attempt an answer (in whatever language). If you want to see what I submitted, look here: Clojure inverse hash.