Category Archives: Software Development

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)

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.

Congratulations Rich Hickey

Rich Hickey, creator of Clojure, has announced his retirement from commercial software development. It looks like he’ll be still active in clojure development, but as an independent developer.

I met Rich in the 90’s when I took his Advanced C++ continuing education course at NYU. I was running a C development team, and we were adopting C++, so a few of us took the class. The most memorable part was the last few sessions where he described a GUI object-oriented design built around a dynamic object system (ala Self or Javascript) using his functor library.

The next 15 years of my career were dominated by C++ where my code was heavily influenced by what I learned in this class.

In 2007, when I saw his presentation at the NYC Lisp group, I reached out to see if he wanted to present to the Western MA Developer Group. Since Clojure was still relatively new, he was willing to come to present to us.

We had about 30-40 people there. One of our members, Chas Emerick, hosted the event. He went on to be a prolific contributor to the clojure ecosystem and co-author of O’Reilly’s Clojure Programming [amazon affiliate link] book.

I helped promote the event by writing my 20 Days of Clojure series. For the time, that was a lot of clojure content.

He came in March 2008 and blew the doors off with an elegant, concurrency-safe ant simulation:

Here is my original write-up of the meeting.

I still keep in touch with many of the developers that were there that day and we still talk about it. I can see the influence in their work.

The most important clojure code I wrote was the code I used to apply to FogCreek/Trello. They gated their application with a programming question, and I answered it in clojure because it looked like I would need something like a BigInteger in my answer, and clojure makes that easy. I also knew that the FogCreek/Trello team liked functional programming.

We might not all have adopted clojure (we opted for F# at Atalasoft and one of our engineers went on to become a Microsoft F# MVP), but our career trajectories were changed by that day when Rich opened our minds to what was possible with modern functional programming.

Thank you Rich and congratulations.

Be Happy When It’s Hard. Be Worried When It’s Easy.

When I was running product development for Atalasoft, I used to love it when a developer was having a hard time with a project. We sold image codecs, and our main competitor was open-source. Our secret sauce was that we could do things they wouldn’t. It was supposed to be hard.

If it were easy, everyone would do it, and it would be free. We wouldn’t have a business.

I think about this a lot when I see what people are doing with Large Language Models. Making an LLM isn’t easy, but Google thinks there’s no moat here for anyone. Still, it’s hard enough because it costs a lot of money, even if you know exactly how to do it.

The part that’s more concerning is what people who use LLM’s are saying. Everyone is so surprised how well it does with basically no work or skill on the part of the user. That’s fine, but then anyone could do it, and the thing they are doing with LLMs isn’t going to accrue value to them.

I think every knowledge worker should be using LLMs in some way, if only to learn about it. It offers enough benefits right now that it can’t be ignored. But, the easier it seems to be that you are getting good results, the more I would be concerned that you won’t be necessary to get those results in the future.

What Hardware Inspires Programming Language Design?

In early computing history, the government, academia, and industry collaborated to create languages for mainframes. The enduring ones were COBOL and Fortran.

You’d think that the PC era, when computing was delivering doublings in power every couple of years that it would also have been a time of programming language innovation. Aside from Microsoft, in the 70’s-2000’s, most of the PC companies didn’t do much here. IBM, Intel, AMD, Apple, Dell, HP, Sony, and Compaq made all the hardware, but contributed nothing to programming languages. Sun is the only hardware company that bucked this trend.

The photocopier, with Smalltalk, did more than the PC to drive programming language design than the PC. Fifty years of OO dominance followed.

But, for driving programming language development, nothing beats phones.

Telephone profits powered Bell Labs, which invented a lot of important tech, but some of the most enduring are the programming languages they developed. To limit it to just the ones I have used professionally, there are C, C++, Awk, KSH and the document processing languages troff, pic, and eqn. I’m probably leaving out about a dozen more.

AT&T was the most prolific converter of phone calls to programming languages, but Ericsson created Erlang, and Apple’s iPhone profits drove them to create Swift. You can give most of the credit for Kotlin to Android.

In the end, it’s probably money that does most of the work, and the real driver seems to be when it coalesces to a single winner.

Third-party Dependencies are Inherently Technical Debt

I think of tech debt as just another kind of debt. You borrow by doing something that causes your code to have features now that it would normally not have for some time if you “did it right”. From then on, whenever you try to move this part of your code forward, you have to pay interest. If you remove the debt, you are paying off principal and future interest payments are lower. There are penalties if you pay off debt for no reason (increased QA and destabilization).

Given that definition, any third-party dependency is technical debt. Third-party? Here’s how I am defining it for the purposes of this article. You are the first party, the platform you are writing on (e.g. iOS, Android, React, NodeJS) is the second party. Everyone else is third party.

Here are some of the ways in which third-party dependencies have debt costs:

  1. You need to constantly update them. Even if you don’t want any of their new features, there might be security patches.
  2. They introduce breaking changes. Since you are constantly forced to update, you might also get breaking changes, which includes new bugs.
  3. They become unsupported. With enough time, this seems to happen to even the most popular projects.
  4. The second-party (your platform) adds their own incompatible implementation. Apple did this to me by introducing better support for HTTP (with many compelling features) which totally replaced AlamoFire.
  5. They don’t update on the same schedule as the second-party. This is not just the major releases, you also run into problems if they don’t keep up with the beta release schedule (you can’t move forward with your own adoption plans).

I have had all of these problems with dependencies I have taken on. Sometimes, it’s worth the trouble, but knowing all of the costs that might come has made me very skeptical of borderline dependencies.

Rewrite of Tech Debt Happens to You and Dependency Based Tech Debt based on Old, Flawed Work is the Jumping Off Point