Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
3
What a start to the year! January was a bit more flat-packed than I think Haley and I would have otherwise liked, but all with great things. (Highlights include: first weekend trip with Lucy, signing a lease on an office, and Third South's annual offsite.) A roundup of writing from the past month: A quick note on nominative determinism, and where Buttondown got its name Lots of media reviews: Catch Me If You Can, Book of Clouds, Becoming Trader Joe (!), We Own This City, Bringing up Bebe, Planes, Trains, and Automobiles, A small snippet on truncating timedeltas in the Django ORM Not as much fun writing in January than I had in the past few months; I promise my backlog of topics is longer than ever before.
yesterday

Improve your reading experience

Logged in users get linked directly to articles resulting in a better reading experience. Please login for free, it takes less than 1 minute.

More from Applied Cartography

What's in a name

Guillermo posted this recently: What you name your product matters more than people give it credit. It's your first and most universal UI to the world. Designing a good name requires multi-dimensional thinking and is full of edge cases, much like designing software. I first will give credit where credit is due: I spent the first few years thinking "vercel" was phonetically interchangable with "volcel" and therefore fairly irredeemable as a name, but I've since come around to the name a bit as being (and I do not mean this snarkily or negatively!) generically futuristic, like the name of an amoral corporation in a Philip K. Dick novel. A few folks ask every year where the name for Buttondown came from. The answer is unexciting: Its killer feature was Markdown support, so I was trying to find a useful way to play off of that. "Buttondown" evokes, at least for me, the scent and touch of a well-worn OCBD, and that kind of timeless bourgeois aesthetic was what I was going for with the general branding. It was, in retrospect, a good-but-not-great name with two flaws: It's a common term. Setting Google Alerts (et al) for "buttondown" meant a lot of menswear stuff and not a lot of email stuff. Because it's a common term, the .com was an expensive purchase (see Notes on buttondown.com for more on that). We will probably never change the name. It's hard for me to imagine the ROI on a total rebrand like that ever justifying its own cost, and I have a soft spot for it even after all of these years. But all of this is to say: I don't know of any projects that have failed or succeeded because of a name. I would just try to avoid any obvious issues, and follow Seth's advice from 2003.

a week ago 12 votes
Becoming Trader Joe

(Shout out to Red Queen Podcast for introducing me to this book.) Becoming Trader Joe is, more than any other business book (certainly any other business memoir), a turpentine book. There is interesting philosophy and retrospective here — and Coloumbe, whether through earnestness or affectation, is a more literate and flippant narrator than your standard post-exit founder — but the meat and the merit of this is the absolute smorgasboard of anecdata and fun facts. Some notes: Trader Joe's counterpositioned themselves early on by making deals with suppliers that their larger competitors couldn't due to supply constraints: they bought, for instance, XL eggs at sub-market prices because the Krogers of the world were unable to purchase them as they wouldn't be able to distribute them to the entire corpus of stores in an areas. This let Trader Joe's both get favorable unit economics and counterposition ("we have this unique product that nobody else has!") Trader Joe's spent a lot of time figuring out how to compete and innovate in an ostensibly static market due to fair trade and other regulations: for instance, being unable to market California wines at a discount (due to price controls) but instead offering complementary wine bank access to accomplish the same thing. Lots and lots of pivoting. (Bullets used to be one of their best SKUs: standardized, value-dense, easily shippable.) Trader Joe's made the explicit decision to shift from "customer focus" to "buyer focus" in order to aggressively maximize deal flow. More than the sheer pleasure of reading — Coloumbe is a fun personality, and garnishes all of his stories with equal parts honesty and flippancy — this book strikes me as real. This is not a hagiography: it does not purport that all of Trader Joe's was a grand plan, but more of a series of strong base hits (with some misses as well) and a couple really great strategic shifts.

a week ago 1 votes
Truncating timedeltas in Django

Consider a fan-out-ish model that you want to aggregate a bunch of: likes on a post, for instance. class Post(models.Model): created_at = models.DateTimeField() class Event(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE) created_at = models.DateTimeField() Let's say we want to aggregate the number of likes on a post relative to the post's creation time (for instance, to visualize the growth of a post's popularity over time), yielding a list of tuples of the form [(minutes_since_post_creation, number_of_likes)]. def test_calculate_popularity_over_time(self): post = Post.objects.create(created_at=datetime.datetime(2020, 1, 1)) Event.objects.create(post=post, created_at=datetime.datetime(2020, 1, 1, 0, 1, 0)) Event.objects.create(post=post, created_at=datetime.datetime(2020, 1, 1, 0, 1, 0)) Event.objects.create(post=post, created_at=datetime.datetime(2020, 1, 1, 0, 1, 0)) Event.objects.create(post=post, created_at=datetime.datetime(2020, 1, 1, 0, 2, 0)) Event.objects.create(post=post, created_at=datetime.datetime(2020, 1, 1, 0, 3, 0)) Event.objects.create(post=post, created_at=datetime.datetime(2020, 1, 1, 0, 5, 0)) results = calculate_popularity_over_time(post) assert results == [ [1, 3], [2, 1], [3, 1], [4, 0], [5, 0], ] We might naively try to do this in-memory, by pulling everything out of the database and doing the math in Python, but that's going to be slow: import defaultdict def calculate_popularity_over_time(post): events = Event.objects.filter(post=post) minute_to_count = defaultdict(int) for event in events: minute_to_count[event.created_at.minute] += 1 return minute_to_count.items() This is an aggregation: databases are good at aggregations! Your first instinct might be to try to do this using ExtractMinute: from django.db.models.functions import ExtractMinute from django.db.models import F def calculate_popularity_over_time(post): events = Event.objects.filter(post=post).annotate( delta=F("created_at") - F("post__created_at") delta_in_minutes=ExtractMinute("delta") ) .order_by("delta_in_minutes") .values_list("delta_in_minutes") .annotate(count=Count("id")) But this has a bug. Can you spot it? Here's a hint: def test_calculate_popularity_over_time_different_hours(self): post = Post.objects.create(created_at=datetime.datetime(2020, 1, 1)) Event.objects.create(post=post, created_at=datetime.datetime(2020, 1, 1, 0, 1, 0)) Event.objects.create(post=post, created_at=datetime.datetime(2020, 1, 1, 1, 1, 0)) results = calculate_popularity_over_time(post) assert results == [ [1, 1], [61, 1], ] The problem is that ExtractMinute, well, extracts the minute, rather than truncates it. Extracting the minute from a duration of 1 hour, 30 minutes, and 5 seconds yields 30, not 90. Whatever are we to do? Well, we can take advantage of EPOCH to truncate the duration of a duration to its number of seconds, and then convert that to minutes: from django.db.models.functions import Extract from django.db.models import ExpressionWrapper, F class ExtractEpoch(Extract): lookup_name = "epoch" def calculate_popularity_over_time(post): events = Event.objects.filter(post=post).annotate( delta=F("created_at") - F("post__created_at") delta_in_minutes=ExpressionWrapper( ExtractEpoch(F("delta")) / 60, output_field=models.IntegerField() ), ) .order_by("delta_in_minutes") .values_list("delta_in_minutes") .annotate(count=Count("id")) I suspect there may be even more elegant and efficient ways to do this, but this satisfies a couple constraints: It delegates the heavy lifting to the database It doesn't require dropping down to raw SQL or registering intermediate tables It's fairly reusable and "Django-ish"

4 weeks ago 23 votes
January, 2025

Hello, 2025! (As always, Death Cab remains the soundtrack by which you should read this.) Headlining this month was my annual review; everything else is flotsam. But, speaking of flotsam: A quick development note on the order attribute in CSS Notes on Linear and Fathom Why Buttondown has an 'alternatives' page Some thoughts on a nascent new newsletter project Letters to a young bartender A warning about hidden settings Only two pieces of media that I finished: Case Histories (meh) and Luck be a Landlord (much better) I have much to write; the backlog is longer than ever before, both literally and figuratively. Our water is out; our daughter is happy; we are keeping sane, and warm, and busy, and we are sleeping lightly but well.

a month ago 33 votes

More in technology

Feature toggles: just roll your own!

When you’re dealing with a particularly large service with a slow deployment pipeline (15-30 minutes), and a rollback delay of up to 10 minutes, you’re going to need feature toggles (some also call them feature flags) to turn those half-an-hour nerve-wrecking major incidents into a small whoopsie-daisy that you can fix in a few seconds. Make a change, gate it behind a feature toggle, release, enable the feature toggle and monitor the impact. If there is an issue, you can immediately roll it back with one HTTP request (or database query 1). If everything looks good, you can remove the usage of the feature toggle from your code and move on with other work. Need to roll out the new feature gradually? Implement the feature toggle as a percentage and increase it as you go. It’s really that simple, and you don’t have to pay 500 USD a month to get similar functionality from a service provider and make critical paths in your application depend on them.2 As my teammate once said, our service is perfectly capable of breaking down on its own. All you really need is one database table containing the keys and values for the feature toggles, and two HTTP endpoints, one to GET the current value of the feature toggle, and one to POST a new value for an existing one. New feature toggles will be introduced using tools like Flyway or Liquibase, and the same method can be used for also deleting them later on. You can also add convenience columns containing timestamps, such as created and modified, to track when these were introduced and when the last change was. However, there are a few considerations to take into account when setting up such a system. Feature toggles implemented as database table rows can work fantastically, but you should also monitor how often these get used. If you implement a feature toggle on a hot path in your service, then you can easily generate thousands of queries per second. A properly set up feature toggles system can sustain it without any issues on any competent database engine, but you should still try to monitor the impact and remove unused feature toggles as soon as possible. For hot code paths (1000+ requests/second) you might be better off implementing feature toggles as application properties. There’s no call to the database and reading a static property is darn fast, but you lose out on the ability to update it while the application is running. Alternatively, you can rely on the same database-based feature toggles system and keep a cached copy in-memory, while also refreshing it from time to time. Toggling won’t be as responsive as it will depend on the cache expiry time, but the reduced load on the database is often worth it. If your service receives contributions from multiple teams, or you have very anxious product managers that fill your backlog faster than you can say “story points”, then it’s a good idea to also introduce expiration dates for your feature toggles, with ample warning time to properly remove them. Using this method, you can make sure that old feature toggles get properly removed as there is no better prioritization reason than a looming major incident. You don’t want them to stick around for years on end, that’s just wasteful and clutters up your codebase. If your feature toggling needs are a bit more complicated, then you may need to invest more time in your DIY solution, or you can use one of the SaaS options if you really want to, just account for the added expense and reliance on yet another third party service. At work, I help manage a business-critical monolith that handles thousands of requests per second during peak hours, and the simple approach has served us very well. All it took was one motivated developer and about a day to implement, document and communicate the solution to our stakeholders. Skip the latter two steps, and you can be done within two hours, tops. letting inexperienced developers touch the production database is a fantastic way to take down your service, and a very expensive way to learn about database locks. ↩︎ I hate to refer to specific Hacker News comments like this, but there’s just something about paying 6000 USD a year for such a service that I just can’t understand. Has the Silicon Valley mindset gone too far? Or are US-based developers just way too expensive, resulting in these types of services sounding reasonable? You can hire a senior developer in Estonia for that amount of money for 2-3 weeks (including all taxes), and they can pop in and implement a feature toggles system in a few hours at most. The response comment with the status page link that’s highlighting multiple outages for LaunchDarkly is the cherry on top. ↩︎

10 hours ago 1 votes
Better Laptop

This post is a quick rundown of assembling my new Framework 13 laptop, why I chose this brand and what's next.

15 hours ago 1 votes
Ed Catmull on Change

I’ve been really enjoying the book Creativity Inc by Ed Catmull of Pixar, it was recommended to me by my colleague Dave Martin a while back and I finally got around to it. There’s an interesting story in it where George Lucas has asked him to develop a film editing system that was digital. While … Continue reading Ed Catmull on Change →

9 hours ago 1 votes
Development is going through a real change

Thomas Ricouard: Is Software Engineering Over as We Know It? Writing code has always been a means to an end, especially in my case, where my goal is to build and ship products to millions of users. What’s really happening is a shift in focus: away from obsessing

56 minutes ago 1 votes
Gustavo Reynaga: Inspiring the next generation of makers with MicroPython

If you’re a fan of open-source technology, Gustavo Salvador Reynaga Aguilar is a name to know. An experienced educator with a passion for technology, Gustavo has spent nearly three decades teaching and inspiring students at CECATI 132 in Mexico. He’s worked with platforms like Arduino, Raspberry Pi, and BeagleBone, and is renowned for projects such […] The post Gustavo Reynaga: Inspiring the next generation of makers with MicroPython appeared first on Arduino Blog.

an hour ago 1 votes