Latest Posts (20 found)
Luke Hsiao 2 months ago

Tip: creating a recurring 5th Sunday event in Google Calendar

I occasionally have the need to create a recurring calendar event on a cadence that isn’t natively supported by Google Calendar. The most prominent example of this is an event that occurs on every 5th Sunday of the month, if one exists. This is very different than the “last” Sunday of the month. One nice workaround that seems to work well is to create an ICS file with the native 5th Sunday syntax and import it. Here’s an example file for an all-day event. The secret is the rule, which expresses the logic we want. Here is another example for an event with a specific time window. Copy-paste the appropriate data into a text file with a file extension and import it into Google Calendar, then you can update the title and whatnot as you please.

0 views
Luke Hsiao 3 months ago

Using a USB switch as a full KVM

I just found a brilliantly easy quality-of-life improvement I needed to share: . I’m the type of person who invests in interfaces . Consequently, I generally like to work at my desk, with my chosen peripherals and tools. Typically, the two most common computers I use there are a work-issued MacBook, and a personal workstation running Linux. I want to switch between them without dealing with a lot of plugging and unplugging of cables. For years, my solution was to use a USB switch with my monitor plugged into both computers simultaneously (e.g., one via USB-C, one via DisplayPort). When I’d switch, I’d click my USB switch to flip my five peripherals (mouse, keyboard, webcam, USB audio interface, YubiKey). Then, I’d toggle my monitor’s menu to switch inputs. This is fine. The USB switch is nice because even if a monitor has built-in KVM functionality, it rarely has enough ports for my use case. They also often have fatal design flaws that make them not very functional. Today, my USB switch broke. Naturally, I took it as an opportunity to revisit the space and see how I could improve my setup. First, I discovered UGREEN makes 2-in, 7-out USB 3.2 switches now. I picked one up and it’s been great. I even have two free USB ports now. Second, and the motivation for this blog post, there are cool software solutions for switching display inputs without touching your monitor settings! If your monitor supports DDC/CI (display data channel/command interface), then it supports being sent commands via software to switch inputs. With that, you can wire up , and now your cheap (relative to full-fledged KVMs) USB switch can effectively function as a full KVM, switching your monitor input, too, at the press of a single button! Simply follow the guide on the repository README . That said, I will leave you with a few tips not covered by the README. Most KVMs, even higher-end ones, do not support very high resolution displays. For example, most will do 4K@60Hz or similar. If you have a higher resolution or refresh-rate monitor, you might have thought that you can’t enjoy the KVM convenience. works by assuming you are just directly wired into your monitor on both computers, so this isn’t a problem! On macOS, it’s simpler to just . But, if you do, the README leaves you with a footgun when setting it up to run at startup. Specifically, the example assumes you have installed it manually and moved it to . Instead, just install it with and update that path to be accurate: For unknown reasons, I couldn’t get to work on macOS. If you find yourself in the same boat, here’s a simple way to get the same information. I suggest so you don’t need to deal with Python virtualenvs manually: Then, paste this into your interpreter: This should get you -like output: For Linux, rather than downloading releases or building from source manually, use . Doing so will let you use tools like to easily keep it up to date with minimal work (via ). Like the case for macOS, you will now need to change the default example to have the right path. By default it will look like . Finally, supports an configuration: But the README notes: The optional settings allows to switch in the other direction when the USB device is disconnected. Note that the preferred way is to have this app installed on both computers. Switching “away” is problematic: if the other computer has put the monitors to sleep, they will switch immediately back to the original input. While this is true, if your monitor and setup support it, using can significantly reduce the latency of the switch. Without it, the chain of delays is: With it, the monitor input switch and the USB handoff happen in parallel. If you frequently switch between two computers at your desk, you don’t need an expensive KVM. A USB switch paired with gets you the same convenience for a fraction of the price. It’s a $500+ KVM-like experience for the cost of an $80 USB switch and a free tool. I’m all about software that reduces friction and makes computing more delightful, and is a great example of that: switching my monitor and full peripheral set is now a single button press, and meaningfully faster than before.

0 views
Luke Hsiao 4 months ago

Using Changesets in a polyglot monorepo

One of the nice things about working in a smaller business is you can enjoy using things that don’t need to scale to extreme sizes. One example in the software world is monorepos . While monorepos can scale well (see Google, Facebook, and others), doing so requires special tooling and infrastructure. With plain , you can only go so far . While you can use it, it has meaningful advantages, like being able to make atomic changes that affect many parts of the system in a single commit, which eliminates whole classes of compatibility and integration issues. You can always split a monorepo later (see ). So, suppose you’re a small-to-medium team using a monorepo. Let’s go further and say that this monorepo stores all your company’s code, meaning it spans many different programming languages—it’s a polyglot monorepo. What tool can you use to manage versioning in a consistent way? I argue that is a solid choice, even if it’s primarily focused on the JavaScript/TypeScript ecosystem. Note There is an open discussion about native polyglot monorepo support . However, even without native support, has the hooks needed to implement this as-is for many setups. For any versioning tool, you are typically looking for how to: assumes per-package semantic versioning (i.e., all packages have their own version). In addition, each package has its own . The team also has a GitHub Action, which importantly allows specifying custom scripts for the and commands. That customization is what gives support for polyglot repositories. In , engineers commit “changeset” files to the repository that define what content ends up in changelogs, and what packages versions are bumped (i.e., major, minor, patch). See the documentation for more details. I’m a fan of . I also really like scripts . The example below uses both. I’m also going to assume you are in a enterprise setting where all your monorepo is private, not open-source. My recommended organization (at least at time of writing) is something like the following. Put all packages in a directory, no matter what language they are. I also enjoy having documentation as code , so let’s say you have a directory, too, and that your docs is written in a javascript-based frontend (like Starlight ), for the purposes of highlighting a nuance later. With this setup, you can configure with a proxy workspace at the root with all your packages. And, declare your dependencies: You should now also update your : Because is built for JavaScript, we also need “proxy” files for all of our packages; uses these to perform version bumps. These can be as simple as: With this setup, note how we are intentionally trying to exclude our internal as a pnpm workspace member—we only want to version packages. To do so, declare the directory its own workspace, otherwise it will try and combine the dependencies into the root . This can be as simple as: Next, we can add our changeset configuration: The glue to create polyglot versioning PRs Next, we want to automate our releases. That is, generating the changelog PRs, bumping package metadata, pushing tags, and triggering builds on those tags. Let’s start with our GitHub Workflow definition, and unpack the scripts it calls. Tip You might be wondering why we run a workflow explicitly, rather than using something like as a trigger. It turns out GitHub has two fatal flaws with that intuitive approach (at time of writing). First, if you push >3 tags at once, workflows will not trigger . Unfortunately, this is a relatively common scenario in a monorepo. Second, GitHub’s triggering of is highly unreliable . This unreliability is still present, even if you use a PAT as they instruct . So, instead consider an explicit for the purpose as I’ve done here. Setting is the key to polyglot support. The meat of the glue for polyglot support then, is how you implement . The key bit here is we rely on to bump the versions in for us when we call , but then it is up to us to propagate that version to the respective language’s metadata appropriately. Here is an example that uses pretty naive parsing. You can write something similar (or better!) for the languages you use. In the standard flow, you will now have a pull request on GitHub with the appropriate updates, as well as the metadata updates for all the relevant packages. Once that is merged, the very same action will run, realize all the files are consumed, and push tags. Note With our example configuration, will only push tags, not publish packages, because we set in our and have all the packages set to . Typically, you’ll then want to react to these pushed tags. For example, to build new docker images. For that, rather than using an trigger as you would reasonably assume, you probably want a . See the tip earlier in the post for why. can manage per-package semantic versioning and changelogs in polyglot monorepos today, even without direct native support for multiple languages. The trick is to treat JavaScript package manifests as the canonical source of version bumps, and then syncing those bumps via your own scripts to the language-native manifests. A few gotchas exist (like explicitly making independent files for subdirectories you want to be independent, or using a separate personal access token to push tags), but none are blockers to being able to benefit from ’s convenient workflow. I used to suggest versioning monorepos with a single global version using a tool like . Since trying , I’m sold on the benefits of letting people write commit messages for future internal engineers while also adding a separate changelog note for end users. These are often two distinct audiences, and relying on a single conventional commit to serve both is often suboptimal. define what content appears in the changelog/release notes influence the version numbers of the packages automate the commits doing the actual metadata bumps and tagging automate the builds that happen in response

0 views
Luke Hsiao 5 months ago

Fixing missing Omarchy boot entries

In a normal Omarchy installation, the expected behavior is that you will see two new boot options in your BIOS: and . There are two for good reason. boots straight to Omarchy, and is what should be first in your boot order, typically. goes to the Limine boot loader menu, which allows you to restore from snapshots , which is useful. However, in my experience, sometimes a fresh install will fail to set up these boot entries. Unfortunately, I have not spent the effort to discover the true root cause. Regardless, fixing it is easy. As a concrete example, I recently installed Omarchy on a ThinkPad T14 Gen 4, and after what appeared to be a successful installation, I had the boot entry, but was missing the one, meaning when I booted, I was wasting time in the Limine menu each boot. The partition was set up as expected, though. And, with that, it’s very easy to fix the boot entries with . First, let’s look at drive names. With these two pieces of information, adding an boot entry is simple. First, you can run to see your existing boot entries. You’ll be able to confirm whether you’re missing entries here. Note I should’ve recorded the output before the fix, but didn’t think I’d write a post about it until after the fact. Hindsight is 20/20. Then, to add a new boot entry you can simply run: So, given the output of , where I see my is mounted to , I know is my drive name, and it’s on or partition . Thus, my specific call would be the following. Same deal if you were missing a Limine entry (using my specifics as an example again). Once that’s done, you should be able to see the boot entries in , as well as in your BIOS. Setting to your first boot option will then boot straight to Omarchy.

0 views
Luke Hsiao 5 months ago

The outsized impact of cultural idiosyncrasies

In the context of companies, cultural idiosyncrasies are fascinating to me. I’m of the opinion that culture (including mission, principles, and values) is one of the biggest factors that differentiate companies from one another. Sure, the product or service is often the largest differentiator, but company cultures—and in particular, their idiosyncrasies—have an outsized impact on public image, internal perception, and job satisfaction. I believe it’s because these idiosyncrasies are evidence of what a company does , and not just what it says it does. They reflect principles and values in uniquely powerful ways to both potential customers and employees. Let me draw some examples from my own lived experience, and I’m sure I’m missing some compelling examples further back (Sun Microsystems, Xerox PARC). Also, and importantly, I’m not saying any of these idiosyncrasies are “right”, but I am saying they all have an outsized impact on my perception of the companies. One of the earliest examples from my own memory is Google. Not only did I experience how Google Search took over the market with its minimalism and effectiveness, but I also grew up hearing all sorts of news about how Google was “the best place to work”. In those articles, did they talk about search? Nope. They talked about cultural idiosyncrasies: free meals from on-site cooks, nap pods, 20% projects, slides in the lobbies, micro-kitchens, laundry services! Oxide is a prime example. They frequently feature in online discussion, but in my perception, the majority of this discussion isn’t about their product; it’s about their culture, often with highly positive sentiment. They find this so core to their company that they dedicated a podcast episode to it . Some examples are demo Fridays, morning water-cooler, no-meet Wednesdays , recorded meetings, dog-pile debugging, RFDs (requests for discussion), no performance reviews, an overwhelmingly writing-focused hiring process , and uniform and transparent pay , to name a few. GitLab may not have the popularity of GitHub, but they have made a strong impression on me for at least two of their idiosyncrasies. The first was particularly prominent during the COVID-19 pandemic: a global, all-remote workforce . They are one of the largest all-remote companies with over 1,500 members in 65+ countries. They are also known for their transparent, handbook-first approach . They publish their handbook for all to see. This is not brief, either. It comprehensively covers everything: engineering, finance, sales, legal, and more. This is an incredible show of transparency and goodwill in sharing hard-earned lessons publicly. Not to be outdone, the 37signals folks also publish their handbook . In their case, they have also published highly profitable books about their idiosyncrasies and culture, such as Shape Up (an engineering methodology that many companies are now adopting), REWORK (unconventional business advice), and REMOTE (on remote work). They also have idiosyncrasies like Omarchy , an opinionated distribution of Arch Linux. They liked it so much they ditched MacBooks as the standard-issue developer laptop and switched to Framework laptops. Tonari is a small Japanese tech startup making some of the most interesting audio/video portals available. Despite being very small, they contribute significant pieces of their stack to open source, such as innernet , a private network system using WireGuard under the hood. They also value a heterogeneous mix of developer machines and were early on the Rust train (note how each of the four engineers mentioned in this blog runs a different operating system: macOS, Arch, Ubuntu, and Pop!_OS). Palantir breaks the mold a bit in this list, because most of the public discussion around it is controversial and focused on the ethics of its business. That said, they are known for their idiosyncratic chaos culture , where “everything is up for debate”—even a random engineer confronting the CEO at all-hands—and where they do almost anything to move fast—like chartering a private jet to Palo Alto to get the engineering team together during the COVID-19 pandemic. People I know who have worked there tell me this is an accurate representation. Famously, it’s the OCaml shop people know about. Compensation so high, even for interns, that it’s an idiosyncrasy. And, most notably to me, they have one of the most interesting tech blogs around. For example, their code review process has such a good reputation that other tech bloggers carve out exceptions for it when talking about code review problems. They care so much about rigor in tests that they not only leverage bleeding-edge strategies like deterministic simulation testing (DST) but also led the funding round of Antithesis, the pioneers in DST . This is highly idiosyncratic. Even for massive companies like Amazon, some cultural idiosyncrasies still have an outsized impact. For example, I suspect most people have heard of Amazon’s 6-page memo approach to meetings. In this approach, attendees don’t read anything beforehand; instead of using PowerPoint, executives sit around a table and read six-page memos in silence before discussion commences. Likewise, Amazon is well known for its two-pizza teams , where the idea is that a team should be no larger than two pizzas can feed. That way it stays small, cohesive, and efficient. Zappos is well known for its unique pay-to-quit approach to making sure the team is invested. It provides an attractive off-ramp for those who are less committed to or interested in the company after their training period, instead of staying despite lacking passion for the work. Patagonia is well known for its Let My People Go Surfing policy, which prioritizes work-life balance, purpose, and employee autonomy. The policy encourages staff to embrace flexibility and spontaneity in balancing work and play. If the waves are good or the powder fresh, staff are trusted to step away, surf, or enjoy nature, returning to work with renewed purpose. Valve is the famously “flat” company, or “flatland”, as their public handbook calls it . They “don’t have any management, and nobody reports to anybody.” They playfully poke at Google’s 20% time by saying that at Valve, it’s 100%. Of course, this comes with its challenges, but this idiosyncrasy has become an integral part of my perception of Valve. Sample size of 1, but I have a friend who really appreciates Vannevar’s $250/month stipend for mental/physical health and $300/month stipend to pay for house cleaners. These dedicated benefits are particularly on point for a mostly remote team. As a company they heavily hire forward-deployed engineers (FDEs) and empower them with very high autonomy. I’m sure this list could be ten times longer with some more thought. But perhaps that supports the point. All of these examples came almost immediately off the top of my head! If you’re running a company, I think it’s important to be intentional about your culture. Both in the mission, principles, and values you profess and in the idiosyncrasies that uniquely evolve because of them. People will talk about your cultural idiosyncrasies to the benefit or detriment of your company. Your employees will reach for your idiosyncrasies when they talk about what is good or bad about working for your company. Your customers will point to your idiosyncrasies as evidence for or against value alignment. Your future employees will compare and contrast your idiosyncrasies with their experiences as they consider their next career move. And random people like me will evidently spend (too much?) time thinking about your idiosyncrasies and trying to theory-craft the perfect set of cultural idiosyncrasies for their hypothetical future company.

0 views
Luke Hsiao 6 months ago

2026 is the year I return to rigorous planning

2026 is the year I return to some rigorous planning and productivity habits I’ve let slip over the years. Specifically, time-block planning (à la Cal Newport) and full-task capture (à la Getting Things Done (GTD) by David Allen). The motivation for this change is that I anticipate this will be an unusually busy year for me at work, at home, and in my communities. I want to make sure I make the most of the opportunities. The motivation for this post is twofold. First, for a bit of documentation and musing on how I have specifically implemented these ideas this time around. Second, for a little bit of self-accountability. Who knows, maybe this will also motivate someone else to reevaluate their productivity habits. While we’re only a week into this year, this is a system I have used extensively since early high school—just less so recently. Consequently, I already know that it is an effective catalyst for my own productivity. I’ll refer you to the links at the top of this post for more thorough exploration on what these systems mean. However, for the purpose of this post, I want to briefly describe these two methods. Time-block planning is a intentional method for planning your days—hopefully a method that leads to being able to do deep work , which I, in agreement with Newport, believe is vitally important in our modern distracted world. This is particularly well-suited to knowledge workers, and it also served me well when I was a student. Many knowledge workers spend their day reacting . Reacting to messages, emails, changing priorities, changing requirements, meetings, etc. This often leads to anxiety, overload, a lack of clarity, a feeling of chaos, and often impedes progress on things that require deep, focused effort. Time-blocking is all about proactive intention . Each day you fill out your calendar with time blocks representing a preliminary plan that gives every minute a purpose. If you get knocked off this schedule, you simply update it the next time you have a chance. This sounds simple. You can get things done in a chaotic environment . This probably isn’t the right term in a GTD context, but it conveys the methodology I find useful. The idea here is that you should capture everything that has your attention (tasks, ideas, etc.) into an external system so that you can free your mind from lingering unproductively and unreliably on them. This capture should be both complete and as immediate as possible, and should go into a single designated spot (e.g., an “inbox”). Then, you can process this list later with intention, either scheduling things, stashing things for later, or intentionally discarding things. If you’re the type of person who frequently has things “fall through the cracks” or finds yourself up at night repeatedly looping through things you need to do, you might find this technique refreshingly freeing. So, now that you have the basic ideas, I want to share my current implementation. Implementation is a highly personal choice. Some people prefer pen and paper (like Newport does). To go further, some people might implement these ideas in the context of notebook-specific systems, like bullet journaling , or a Hobonichi Techo . Others might use special, purpose-built apps on their computer or phone. Others might use intentionally basic apps or systems like todo.txt . Others might use a big whiteboard in their office. While those who know me know I love stationery, I’ve found that the single most important factor for success with these systems is low friction. If I remember a task in the middle of the night, and that means I need to go find a pen and my notebook, it’s not happening. But I can always find my phone for a quick note. So, I use Google Calendar and Google Tasks. They are already on my phone. They are easily accessible on both phone and computer. They have sufficient features to implement the systems. A good work week usually looks something like this. Note I have several Google accounts, so they all feed into a master view on my phone that includes time blocking before and after work hours. While the image above shows a fully-populated week, I actually only time-block one day ahead. That is, during the “shutdown ritual” of the day, I review the tasks and ideas I may have captured within the context of my current task lists, then time-block the next day based on those tasks. One exception here is recurring events are scheduled as recurring, and thus are already populated on their respective days. While the screenshot above doesn’t show this well, it’s also important to have some empty buffer time to absorb unexpected events, or variance in how long things take. This is another advantage of digital over paper: I can easily schedule events far into the future and not lose the information. With a paper planner, it’s hard to schedule someone’s wedding in a year. As the day proceeds, if things do not go according to plan, I adjust the blocks on the calendar to reflect reality. This is a valuable exercise both as an artifact to review where I actually spent my time on a given day and as useful feedback to improve my time estimations. For example, you may notice I spent about three full days testing in the screenshot above. I had initially anticipated that would take a single day. The act of making things reflect reality is a good way to calibrate myself on how long to expect for similar work. It goes the other way too: sometimes things take much less time than expected. When it comes to deep work, it feels good to see those larger, uninterrupted blocks of time. If those blocks were short and highly interrupted, I’d take it as a signal to make adjustments that allow me to do deeper, more meaningful work with less context switching. You can see how time-block planning would work effectively in a knowledge worker’s or student’s life, but likely not in the life of an emergency room nurse. The last time I used Google Tasks was a few years ago, and they’ve made some nice improvements since. For the purpose of full task capture, I keep a relatively simple system. Not quite as simple as a , but still minimal while offering nice organization features like recurring tasks, scheduled tasks, and sub-tasks. I organize Google Tasks somewhat Kanban-style. Google Tasks has a default list you cannot delete. I use that as my “inbox”, but I title it “Maybe?”. All quick notes go into here for later processing if I don’t have time to put it in the right list immediately. If I decide to do it, it goes into the appropriate list. If I don’t, it gets deleted from here. I sort this with “My order” sorting so I can arbitrarily sort it with highest priority at the top. On the left, I have my “Not now” list. These are things I want to do at some point, but they aren’t urgent. I just don’t want to lose them, so this is much like a backlog. This is also sorted “My order”, with highest priority at the top. In the middle, I have “In progress”, which could also be named “To do”. These are things I’m actively working on. Everything in this list has a date indicating when I intend to work on it. I might add deadlines if relevant. I sort this list by “Date” so that the things I need to do today are always up top, and I can quickly see what I need to do next. This is the only list that I mark things as “complete” from! Conveniently, these also show up in Google Calendar as you do them, providing a useful artifact of tasks you completed that day. Next, I have “On hold”. These are things I was working on, but are now blocked for some reason. A holding area. When they become unblocked, they will go back to “In progress” with the appropriate dates. Sorted in “My order”, with highest priority at the top. Finally, “Ideas”. This is just a list of ideas sorted in “My order” with most interesting at the top. I also find it handy to jot down things like gift ideas for people in the moment I discover them. When I was a student, this was a superpower; I still give the advice today . As a student, I would start each semester by inputting all coursework dates from every class syllabus. I always knew what was due, which weeks would be heavy, and which would be light. Another common question from fellow knowledge workers is how this fits in with other task tracking systems you might have at work (e.g., Linear, Jira, etc.). The answer there is that this is far more lightweight. I’m not writing lengthy descriptions or logging progress and notes on these items. These are written as “just enough” context to know what they refer to clearly. This is a personal system that complements the systems your teams might be using. I should never have stopped doing this! When you develop a productivity system that works for you personally, it feels like a bandwidth multiplier. There is also much more to discuss about setting goals, having a vision for your career , and other high-level planning that ultimately feeds your day-to-day work, but those are out of scope for this particular post. This is all about the nitty-gritty of day-to-day productivity. This system might not work for you, but I hope it has sparked some ideas as you work on your own systems.

1 views
Luke Hsiao 8 months ago

Switching from GPG to age

It’s been several years since I went through all the trouble of setting up my own GPG keys and securing them in YubiKeys following drduh’s guide . With that approach, you generate one key securely offline and store it on multiple YubiKeys, along with a backup. It has worked well for me for years, and as the Lindy effect suggests, it would almost certainly continue to. But as my sub-keys were nearing expiration, I was faced with either renewing (more convenient, no forward secrecy ) or rotating them (rather painful, but potentially more secure). However, I’ve realized that I essentially only use these keys for encryption, and almost never for signing. So, instead of doing either of the usual options, I’m going to let my keys expire entirely. I’m now experimenting with , which touts itself as “simple, modern, and secure encryption”. If needed, I will use for signatures. This required changing a couple of things in my typical workflow. First, and foremost, I needed to switch from to , a fork of that uses as the backend. This was actually surprisingly easy because includes a simple bash script to do the migration. There is no installer for , and no Arch packages. But it’s easy enough to install because it’s just a shell script you can throw on your . Note that for Arch, I also needed to install , which it assumes you have. I also name as on my machine. The benefit of this is everything that had integration has continued to “just work”. For example, , my email client of choice, behaved exactly the same after the migration. I would occasionally use as my SSH agent on my machines. It was convenient. However, I also like the idea of having a dedicated SSH key per machine. It makes monitoring their usage and revoking them much finer-grained. The absence of forced me to set up new keys on all my machines and add them to various servers/services. Easy encryption with chezmoi While I was tending to the encryption area of my personal tech “garden”, I also started leveraging chezmoi’s encryption features . I already use for my configuration files, but with encryption, I could also easily add “secrets” to my public dotfiles repo . In my case so far, this just means my copies of my favorite paid font: Berkeley Mono . also has a nice guide for configuring chezmoi to encrypt while asking for a passphrase only once for . I was also very pleasantly surprised with how easy it was to switch to ! Last time I set up the GPG keys on my YubiKeys, I spent several hours. This time, with the help of and embracing the idea of having unique keys on each YubiKey, but encrypting everything for multiple recipients, setting up my keys was surprisingly trivial. It also generates the keys securely on the hardware key itself, which is nice. The whole process probably took 30 minutes. It was so easy that in the future, I’m very much not intimidated by the thought of rotating keys. Did I need to switch to ? Of course not. However, over my career, I repeatedly find that exploring new tools for your core workflows (part of investing in interfaces ) is just plain fun. I often learn new ways of thinking about problems. Sometimes, you walk away with a new default that brings some fresh ideas and some delight to your life. Other times, you walk away with your trusty old tool, with greater appreciation for its history and the hard-earned approach it has established. For my uses at the moment, definitely falls into the former camp.

0 views
Luke Hsiao 8 months ago

First impressions of jj from a git fan

Jujutsu ( ) is a distributed version control system (like , , , , etc.). However, it is rather unique in that it is -compatible—it uses as a storage layer, meaning you can use it right now on your existing repos without disrupting anyone else. It also provides other features, such as It also clearly has a (currently small but) passionate group of users, which is a good sign of a useful tool. Some of these users strongly dislike , finding its user interface unintuitive and clunky. I, however, am very much not part of that group. I love . Over the years, I’ve curated a configuration that I find a joy to use. I’ve built up years’ worth of muscle memory. As a result, even though I love investing in interfaces , such as new software tools, I haven’t ever felt motivated enough to try . That changed this past week when I learned that Steve Klabnik found so interesting that he’s leaving Oxide to pursue it further . As an experiment, I’ve committed to using only for at least a couple of weeks. As a user, the tutorial that clicked best for me is Steve’s . So, if you’re a fan, I suggest starting there. What I miss from I quickly ran into two features that I wish were supported. First, does not support submodules . Sure, submodules are not great in many cases. However, one very common case I use them for is Zola themes, such as the theme for this blog. My customization lives in my repository, and the theme for the site lives in a submodule. However, it looks like they are working on that actively and with great care. Second, I missed . This one is more minor, because I can always just fall back to to do so. Even in just a few days, I’ve wanted to serialize a patch to a file (i.e., ) or apply some patch I have sitting around with . One example is a patch file I keep around that customizes an environment for debugging purposes. As a -native approach, I’ve been keeping a change separate that I can rebase into my patch series and move out later if needed, but that feels clunkier. Then, there are a handful of more rarely used things that I have not needed yet, and consequently have not figured out equivalents for. For example, I’m not sure about an equivalent for view release notes via tags. With , I used an alias like But, then it seems right now doesn’t support anything but simply listing tags right now. No creation or deletion. Ouch. I’m not sure an equivalent for . Most likely would need to build some template. I don’t have some equivalent of . But again, it’s -compatible, so I can just keep using . I’m sure there will be a long tail of things like this. Steve and others have described as “both simpler and easier than git, but at the same time, it is more powerful.” I’m not sure I fully agree. In some ways, it has better defaults (e.g., , ), but in other ways, it is complex (e.g., revset language). That said, I do see how it is more powerful. You cannot exactly your mental model to directly onto ’s. That said, one of the main things I enjoy at $WORK is that for a repository, ’s default “view” (for lack of a better word) is a branch, whereas ’s default view is much higher level. I find ’s approach more intuitive: it’s always pretty trivial to see what branches/PRs I have ongoing at a glance (i.e., with ). It also means you can easily do fun stuff like rebase all of your branches at the same time, and push them all simultaneously to your remote! This is made even better by the fact that conflicts are first-class objects, meaning you can rebase all your branches, and deal with conflicts later. In a similar vein: emphasizes commits or changes in a way I find pleasing. Even though in I have ways to do similar things, does indeed make them easier. For example, splitting one commit into multiple is easier. In , you’d probably do something like In , just does the more intuitive thing. Instead, the same process looks more like As another example, I’m a huge fan of . It makes folding follow-up changes into an appropriate earlier commit easy and effective. However, it isn’t built into . Meanwhile, ships with , which does the same thing. I found that in , I was already following a workflow close to the squash workflow recommended by ’s creator, Martin. The main thing I still feel clumsy with is named bookmarks (similar to branches). Specifically, I find it hard to remember to the bookmark when I make updates, and how to rebase them without looking up commands. That said, it’s becoming more familiar over time. Much like , I’ve also found that ’s defaults aren’t entirely sufficient for me. I needed to make some tweaks. You can see my up-to-date dotfiles here , but as a snapshot, these are changes I found to be particularly valuable right from the start. First, I strongly prefer over the default viewer. also makes it easy to view with another tool with . For example, I also like using . I added a config from this discussion so doesn’t clear the screen on quit. Second, I like having my conventional commit template automatically populated on . I also have the diff included , which mimics the config I had with using Third, I changed the default bookmark prefix to . Finally, I added a bunch of aliases I use frequently. I use to copy-paste commit descriptions into PR descriptions. I use to view all the logs most relevant to me. This was discovered courtesy of Will Richardson . I use to view all logs. I use and to push all my bookmarks, and rebase all my changes on main, respectively. Finally, I use to move my bookmark to the latest change (I found this one thanks to Shaddy ). I actually find quite fun! Again, I do not think it “solved” any pain points I had with . I was already a very happy user. Ultimately, ’s interface and mental model do feel refreshing enough that I’m likely to continue using it. conflicts as a first-class object no explicit index revset language operation log and powerful undo automatic rebase and conflict resolution

0 views
Luke Hsiao 9 months ago

ASUS PA32QCV: a programmer's first impressions

Those who know me know that I invest in interfaces . Previously, I had an LG 38GN950-B (38“ Ultrawide, 3840×1600 @ 144Hz). It was expensive, but had a little extra vertical resolution vs its competitors, and the high refresh rate was great for casual gaming. But, over the years, I play games less, and my eyes are aging! I find myself appreciating larger and more crisp fonts for programming. With that in mind, I started looking for a high resolution “retina”-class display. There are only a handful of these around at the time of writing. In the 27“ 5K group: Apple Studio Display / ProArt PA27JCV / Samsung S9 / Kuycon G27P . Then, there is the 32“ 6K group: Apple XDR / ProArt PA32QCV / Kuycon G32P / Dell U3225KB / LG 32U990A-S . Then, there is the even more sparse 8K group: Dell UP3218K / ProArt PA32KCX . I know I wanted to avoid fractional scaling, so I was searching for either 6K or 8K. I immediately discovered that 8K is way out of my price range. In the land of 6K, this ProArt PA32QCV met a sweet spot: (1) being available for purchase right now, (2) having relatively modern hardware, and (3) not being ridiculously expensive. So, I bought it. I’m a big fan. As soon as I switched, I understood why people like these high-resolution displays. Text is crisp and delightful to look at! I do not miss the ultrawide at all. I run at 2× scaling, and have been loving looking at it. Yes, I do notice the drop from 144Hz to 60Hz. However, because I’m mostly programming, I really don’t notice it during a typical workday unless I go looking for it (e.g., dragging a window quickly). The colors look great. In other words, when it comes to the display itself, it seems fantastic, which is what I was looking for. Some people complain about the matte “LuxPixel” finish. I think it looks fantastic, and appreciate the lack of glare. This is a bonus, not an issue. When it comes to some of the other features, they are notably less useful. These are fairly awful, as one would expect. What confuses me is why these are here at all. This is a “pro” monitor, and I see no value in having speakers. I wish they saved some cost or reduced size/weight instead. I frequently multiplex between a work laptop and a personal desktop. So, a built-in KVM is actually very appealing! However, I’ve never found one that works well for my needs, so I’ve always reverted to separate display cables and a USB switch. The same is true here. Yes, this monitor has a KVM, but it does not have enough USB ports for my needs. It has 3 USB ports, and 1 thunderbolt port. The good news is that this lets me use 4 USB devices just fine on MacOS. The bad news is that the 1 thunderbolt port does NOT work on my desktop, leaving me with just 3 devices. Typically, I want four: a mouse, keyboard, webcam, and audio interface. Five, if you count a security key. So, good idea and will work for just a keyboard an mouse, but not enough for me. Another fatal flaw is that the KVM doesn’t appear to activate immediately upon switching input. This is vital because if your other machine goes to sleep, the input button will not switch until there is a display signal on that input—a signal that you have no way to initiate without a keyboard or mouse active! With a USB switch, that means I typically switch, wake up the machine, and then switch the input. With the built-in KVM, that means there is simply no way to actually switch without waking up the machine first some other way. Not cool. The “Light Sync” feature actually sounded great: I work right by a window, and frequently do adjust brightness to compensate for the ambient light. It would be awesome if it could do it for me! However, after using it, as far as I can tell there is no way to set the baseline/target brightness you want. Instead, it ranges from acceptable (in color preset) to unusably dim (in ). It is a good idea, but the implementation seems to only be viable in mode of the ones I tried, which means this forces you to make a color preset tradeoff. Instead, they should’ve just let me set the target brightness. Some features/design choices I was pleasantly surprised at. Not surprised they existed, but surprised how much I liked them. The box came with the display itself, a stand, a Thunderbolt 4 cable, an “ultra 8K” HDMI cable, and a power cable. After reading Michael Stapelberg’s review of the 6K Dell , I was a little worried about compatibility. I was optimistic that with a brand-new release, more compatibility issues would’ve been ironed out. I have two machines I use with this display: an M1 MacBook Pro, and my custom workstation (Radeon RX 7800 XT), running both Windows and Linux. The monitor was clearly built with a MacBook in mind. Using the Thunderbolt 4 cable, I instantly got power and display—full 6K no problems. Colors do indeed seem highly matched with the color preset. On my desktop with Windows via the included HDMI cable there was no problem. It instantly recognized full 6K@60Hz. My desktop with Linux via the included HDMI cable? No 6K at all. I first went down a bit of a rabbit hole assuming it was a Linux software issue (the same hardware works with Windows!). But, I’m running Arch, with a very recent kernel and all up-to-date drivers. I decided to try a DisplayPort cable instead. Whew! Instantly recognized the 6K@60Hz. So, that’s the tip: use DisplayPort on Linux. So far, after a couple days of use, I’m very pleased. We’ll see how it fares long term. I really like buttons being on the front of the monitor, rather than needing to blindly click around on a side or bottom. No giant power supply! I appreciate the saved desk space of having the power supply built-in.

1 views
Luke Hsiao 9 months ago

AI etiquette

I recently read a great blog post from Alex Martsinovich . It expressed a concept that I’ve been feeling, too: proof-of-thought . For the longest time, writing was more expensive than reading. If you encountered a body of written text, you could be sure that at the very least, a human spent some time writing it down. The text used to have an innate proof-of-thought, a basic token of humanity. Now, AI has made text very, very, very cheap. Not only text, in fact. Code, images, video. All kinds of media. We can’t rely on proof-of-thought anymore. Any text can be AI slop. If you read it, you’re injured in this war. You engaged and replied – you’re as good as dead. The dead internet is not just dead it’s poisoned. So what do we do? Luckily for us, AI only talks in response. Unlike Earth, AI does not emit comedy sketches into outer space on its own. To get AI slop, somebody needs to ask for it. To send it further, someone needs to retransmit it. Our problem is other humans, really. There’s nothing wrong with using AI. When you do, you know what you’re getting. The transaction is fully consensual. But whenever you propagate AI output, you’re at risk of intentionally or unintentionally legitimizing it with your good name, providing it with a fake proof-of-thought. In some cases, it’s fine, because you did think it through and adopted the AI output as your own. But in other cases, it is not, and our scrambler brain feels violated. He goes on to propose a simple new sense of etiquette: AI output can only be relayed if it’s either adopted as your own or there is explicit consent from the receiving party , otherwise, it is horribly rude. I’d add that essentially, you either are providing proof-of-thought, or, you’re explicitly acknowledging the lack of it. This resonates. It reminds me of Neven Mrgan’s reflection on how it feels to get an AI email from a friend . Recently I received an AI-written email from a friend. It wasn’t sent to test AI, or to show it off, as in “ha ha check this out”; my friend had a question to ask me, and the email asked it over the course of a few paragraphs. It then disclosed that, oh by the way, I used AI to write this. My reaction to this surprised me: I was repelled, as if digital anthrax had poured out of the app. I’m trying to figure out why. He then really breaks down how it did and didn’t feel as a recipient. For example, it didn’t feel like they were using it as an autocorrect to type better. It did feel like a family fridge decorated with printed stock art of children’s drawings. Years from now, could an AI that was trained on all of my friend’s emails and texts and personal documents sound convincingly like them? Could it be so advanced that I wouldn’t even be able to tell that my friend hadn’t written to me at all? Possibly. And that idea saddens me the most. The words on this blog have been and will always be, manually and intentionally typed by me (on a keyboard that delights). Yes, I am a big em dash (and en dash!) user—I even have macros in my editor to make the substitutions. No, that’s not AI. Yes, you’ll find typos and grammar errors in my posts; I’m human (but if you do, please send me a note so I can fix it). Still, I find writing a good way to clarify my own thoughts and, while I primarily write for myself, occasionally someone else benefits, too. While we have no formal way to prove proof-of-thought, my pledge to both you and myself is that this place will remain a place for human thought.

0 views
Luke Hsiao 10 months ago

Repairing my Windows bootloader after Omarchy

Before I switched to Omarchy, I was running on one NVME drive, and Windows on another. Part of this is because I want to use full disk encryption, and part of this is because I was originally playing with Project Bluefin , which doesn’t support dual-boot off of a single disk. I installed Omarchy via online ISO, and pointed it at my Linux drive. After it installed, I no longer could boot into Windows. My BIOS didn’t even show the 2nd NVME drive as a boot option. After some digging, I figured out a way to get things back without needing to reinstall any operating systems. My system looked like this after the Omarchy install: That is, the , partition with the bootloader was on , and it didn’t seem there was any corresponding partition on . The good news is that this indicated that I could probably just restore the required boot files to for Windows and be on my way. To do so, I used Hiren’s BootCD PE on a USB drive with Ventoy . Once in the Windows PE environment, we can restore the files with and in the command prompt. Now that we’ve assigned a letter, we can fix the files: where specifies the volume letter of the system partition, and specifies the firmware type ( copies all of them). At this point, I could once again boot from this drive and land in Windows. But, we could do even better! Having to use the BIOS boot menu is annoying: you typically have to spam an -key at startup, and if you miss your opportunity, need to restart again. It would be nice to be able to just boot to the same bootloader and select Windows, like in a traditional single-drive, dual-boot setup. Turns out that isn’t hard, since Omarchy uses Limine out of the box. In Linux, we can just edit the boot config. Then, I added the following entry after the Omarchy ones, and before the EFI fallback. With this, I then configured my BIOS to with Limine as the first boot option, and disabled the others. Now, it boots to Limine on startup, and my Windows drive is once again an easy option to select. Nice.

0 views
Luke Hsiao 10 months ago

Berkeley Mono Variable (TX-02) in Ghostty

Inspired by Michael Bommarito’s post , I’m just dropping some quick notes on getting Berkeley Mono Variable (TX-02) to work in Ghostty. Specifically, Berkeley Mono , released on 2024-12-31, running in Ghostty . Using the variable version of the font is highly convenient: it is very fast to change styles and tweak things until it is exactly how you like it, without having to iterate with installing static fonts. I suggest you read his post first for lots of nice context on fonts and their features. Then, the key bit of information I needed to get this working was the following. TX-02, the updated version of Berkeley Mono, has different OpenType features than the original. There is no documentation I could find on exactly what they mean, but via some trial an error, I’ve landed on the following config. Specifically, I found that and appear to change what the stylistic sets do (to different things than my comments). , , and don’t do anything that I noticed. So, effectively, it seems to me that the only two features you actually care about are your settings, and then whether you want ligatures with , and what style of / you want via stylistic sets. Another tip: if you have static versions of Berkeley Mono installed, I noticed that that sometimes breaks Ghostty from loading Berkeley Mono Variable. I’m unsure why, but I was able to resolve it by removing the static fonts, configuring things, and then putting them back.

0 views
Luke Hsiao 10 months ago

The youngest sibling advantage

I am the youngest child in my family. I’ve heard a lot of discussion around the stereotypical traits of the youngest child. In fact, many of these are so commonly echoed that they have their own name: youngest child syndrome . Sure, some of these arguably apply to me (certainly my older siblings would label me spoiled), and others do not. But, what I haven’t heard in many of these discussions is what I consider to be the biggest single advantage: life scouts . No, not like Boys Scouts of America “Life” scouts, like people scouting ahead for you in life. The youngest child often has older siblings they have observed keenly, maintained a strong and open rapport with, and with whom they have shared significant life experiences. They know them at a profound level. They then get to observe these older siblings blaze a variety of trails through life, years ahead of them. The youngest get to observe the decisions the older siblings make, often well aware of context, and then see the consequences of those decisions play out. Sometimes those consequences are positive, and sometimes they are not. Sometimes the outcome seems causal, and sometimes it seems that luck played a large role. Sometimes that whole decision-consequence cycle can happen well before a younger sibling might be faced with a similar decision. In many ways, this is like mentorship on an amplified level. Not mentorship in terms of career or school advice, but mentorship in life—hard earned advice and examples in real time, with real stakes, resulting in real impact. The advantage of having life scouts naturally follows. The youngest now has deeply detailed experience of highly trusted people to draw from when navigating the world ahead of them. As a kid, you already know the best parks or playgrounds nearby. You also know what areas of the neighborhood are dangerous. As a student, you’re confident in navigating the school system, and the trade-offs of different schedule choices. As an adult, you have gleaned some insight on dating and partner choices, and how those can affect directions in life. You have observed different fields of study or career choices, and how those areas affect stress, income, flexibility, and more. You have witnessed glimpses of different parenting styles and parenting choices. The examples are endless. Sure, not everything directly transfers, but I’ve found these “life scouts” to be priceless in my life. A shout-out to all the older siblings out there who pave paths and building organizational knowledge for those of us who come afterward. We appreciate you! As for me, I think benefiting from such mentorship (from siblings) is also why I value mentorship in general so highly. As the youngest sibling, I might not be able to provide powerful mentorship this particular way, but I still can hopefully pay it forward to nieces and nephews and other youth in my community.

1 views
Luke Hsiao 11 months ago

Comparing UTOPIA ISPs

I currently use UTOPIA for my fiber connection. Their model is that they charge for the infrastructure, and then they have many ISPs on top that provide competing internet services. As a snapshot comparison (primarily for my own notes), here’s a comparison of pricing for residential service, on the tier of 1Gbps download and upload (symmetric). This is the tier I’m currently on. At some point, I want to go through and learn more about their nuances. For example, XMission has an annoying trait of running their own firewall, which breaks websites occasionally. But, they also run their own datacenters and have some nice peering agreements that likely improve latency and performance. Here’s a UTOPIA-provided comparison sheet , too. There is some discrepancy in the pricing and the ISPs themselves, though.

0 views
Luke Hsiao 1 years ago

Variable fonts and italics across browsers

I’m a big fan of Atkinson Hyperlegible Next and Berkeley Mono (TX-02). It is my opinion that they both look great, unique, and provide valuable legibility features. I recently refactored this site to use the variable, web versions of these fonts. I was happy that I now had only a single definition, not a handful, and went on my way. (I had haphazardly picked up these parameters on the Internet somewhere.) Then, just recently, I realized that my italics were broken on this site for weeks! They worked on Chrome as I had it configured. They did not work on Firefox. I tried a few things. First, I tried duplicating the definition, but with . This made it work on Firefox, but broke Chrome. I then found there is a new way to specify italics for variable web fonts, specifically (assuming it is supported by the font): . In the case of Atkinson Hyperlegible Next, I could get my italics looking right on both Firefox and Chrome by adding the following CSS. But, this didn’t work for Berkeley Mono. I also tried things like using in my definitions and other little tweaks to no avail. Finally, after more experimentation, I was pleasantly surprised that I could get everything working just by simplifying . I dropped the (likely incorrect) attribute. No need for the class override, now, either. With that, I have italics , both for my normal font, and for my font that work on both Firefox and Chrome. All I needed to do was delete the right two lines.

0 views
Luke Hsiao 1 years ago

Performance gains of undervolting a T480s

I have been using a self-refurbished Thinkpad X230 for quite a while as a personal machine. I believe in the benefits of continuing to use old laptops [ 1 , 2 , 3 ]. However, recently, I’ve started to need to use a laptop to do presentations frequently, and the HDMI port on my X230 is not functioning fully, resulting in distorted colors and other distracting artifacts. I might try and repair that, someday. However, due to pressing need, I instead picked up a used T480s from a good friend, which had a working HDMI port. Of course, the first thing I did was install omakase-blue . Over the first couple days of use, I noticed that this thing ran hot . Sure, I have the version, but the fans were spinning up far more than I’d like, and CPU temps would frequently exceed 95 °C for bursts at a time. Naturally, I did a little digging. It turns out, Linux and the T480s have a longstanding problem! I don’t understand the problem well enough to explain it here. But, essentially, people were seeing throttling and temperature issues with these machines on Linux (but not Windows). To be clear, this issue was found on an old Linux kernel (4.15, I’m on 6.14). I do not know if this specific problem is affecting me. However, through this, I learned about a fun tool for undervolting a T480s: . Even if this issue was resolved in newer kernels, I imagined that some undervolting could still help my thermals and performance. So, I tried it. In addition, to give the attempt the best chance it had, I also replaced the thermal paste with fresh Duronaut . I was surprised to find a 12% improvement in single-core performance and a 24% improvement in multi-core on Geekbench 6! The laptop runs much cooler, and the fans spin up much less. It’s much more pleasant to use. I ultimately landed on the following undervolt for this machine. (Remember, each chip is different, you might not be able to use these values.) What a satisfying way to squeeze a little more performance out of a still-great laptop.

0 views
Luke Hsiao 1 years ago

Steel man arguments, or arguing intelligently

I’m a big fan of the idea of “steel man” argument. However, I’ve found that this term is far less widely known than its “straw man” counterpart. I’ve heard some people just call this “arguing intelligently”. Here are two good definitions if you’re unfamiliar with the idea. First, Wikipedia’s for the more abstract definition, and then Robin Sloan’s practical example. Wikipedia : A steel man argument (or steelmanning) is the opposite of a straw man argument. Steelmanning is the practice of applying the rhetorical principle of charity through addressing the strongest form of the other person’s argument, even if it is not the one they explicitly presented. Creating the strongest form of the opponent’s argument may involve removing flawed assumptions that could be easily refuted or developing the strongest points which counter one’s own position. Developing counters to steel man arguments may produce a stronger argument for one’s own position. Robin Sloan There are two debaters, Alice and Bob. Alice takes the podium, makes her argument. Then Bob takes her place, but before he can present his counter-argument, he must summarize Alice’s argument to her satisfaction — a demonstration of respect and good faith. Only when Alice agrees that Bob has got it right is he permitted to proceed with his own argument — and then, when he’s finished, Alice must summarize it to his satisfaction. The first time I saw one of these debates, it blew my mind. Or, as Stephen R. Covey put it: “ Seek first to understand, then to be understood ”. Imagine if the default mode of debate was like this. A tool for persuasion, consideration, exploration, and achieving consensus (or at least clearly understanding the shape of disagreement). Instead, culturally, we’re more used to debate as a stage for name-calling, politics, distraction, point-scoring, and drama. Next time you’re debating something, consider arguing more intelligently, and treating the other parties with that same human respect.

0 views
Luke Hsiao 1 years ago

Sharing intro cards

Those who know me know that I am fairly introverted. But, I am also someone who strongly values community, and thinks there are huge benefits to be had by building or connecting to one. A big part of building or integrating into communities is getting to know the people in them. Some people have a natural talent for this. Others have built that skill through many years of deliberate practice. There are also some people that use software to manage and track these relationships “personal CRM”-style (e.g., Monica , or really detailed notes in Google Contacts). I am currently none of these people. But, I would like to build the skill. One reason knowing people is valuable is because it helps facilitate connection. “Oh, I know is in that industry, let me connect you.” “The family has kids that line up with your kids age and go to Junior High, too.” “Oh used to live right around there, let’s ask them for advice.” These types of serendipitous connections can build strong bonds and strengthen a community, but you can only make them if you know enough. To that end, I think “intro cards” are an interesting idea. Imagine that when you moved in somewhere, or went to a new group meeting, or church event, or met with the other parents for your kid’s soccer team, that you could get a small physical card of pertinent information about these new people you met. In big social settings like that, where you’re meeting a lot of new people, it’s very easy (for me, anyway) to have forgotten the names of the people I just talked to. Now, instead imagine that throughout the event, you were exchanging intro cards, something like the following. During the event, you could focus completely on the current conversations, knowing you already have some notes provided to you to recall the previous ones. Is the concept so wild? In Japan, there is a whole interesting culture around exchanging business cards . In the US, we rarely do so. Japan recently went even further, making a whole trading card game for the middle-aged members of their small rural town, connecting the youth to older generations in a heartwarming way. Naturally, these intro cards might include personal information you probably wouldn’t put on the Internet, but yet might want to provide to people depending on the context. So, of course, you should tune the content to the purpose. You should also feel things out first, before you just go handing these to everyone. For example, in the context of introducing yourself to neighbors, maybe it is something like Bart’s above. If it’s meeting coworkers, maybe it highlights parts of the system you’re familiar with, or your favorite software or languages. If it’s a hobby group, maybe it highlights more about your specific niche interests. I’ve started trying this idea out in a few scenarios, to positive reception. To make it easy, just print these on 3“×5“ index cards at home. An example Typst document for it is included below.

0 views
Luke Hsiao 1 years ago

Teammates, not coworkers

I was talking with a friend a while back who had gone through some jobs they didn’t enjoy, and had now landed on a job they seemed to thoroughly enjoy. When I asked them about it, the key reason they provided for why this particular job seemed so much better was: “I have teammates, not coworkers.” That phrase has resonated with me since. What distinguishes a “teammate” from a “coworker” in a way that improves job satisfaction like it did for my friend? Here are some thoughts. I believe explicitly listing principles and values is incredibly valuable in all sorts of contexts . I particularly think that is true in the context of a company. Many , many companies do this , and know that it is a meaningful way to differentiate themselves to potential applicants as well as provide a guiding force for the business. Sometimes it is under different names, but the purpose is generally the same: broadcast their principles, values, and way of working to attract like-minded teammates. It also goes without saying that professing principles/values doesn’t stop a company from being evil (see Enron). Sometimes, values can come into tension with each other, and that is healthy. But, it’s hard to be a good teammate when there is not a large overlap in values. Instead, it will be a consistent source of friction. Fundamentally, I think this is the key point. Then, depending on what those shared values are, it’s easy to see some concrete examples. Here are some examples that I think are teammate behavior drawn from my personal values . Entropy in engineering shows up a lot of different ways. It could be the codebase (i.e., the software architecture, the abstractions, the tooling, etc.). It could be communication (i.e., messages that lack sufficient context, require unnecessary back-and-forth, etc.). It could be all sorts of other things (documentation, company policies, IT management, and more). I’d also classify complexity in general as a component of entropy, despite the fact that growing complexity is often fundamental to a developing product. I define complexity as APOSD does : “anything related to the structure of a software system that makes it hard to understand and modify the system”. This generally pragmatic definition also applies to things outside software. Entropy is the extra noise, disorder, confusion, etc. I’ve found that people I’d call teammates naturally reduce entropy (and strive to contain complexity) in their respective domains. Meanwhile, coworkers are often the source. Some examples: Teammates are the type of people that actively educate and build the collective skills of those they work with. They are the subject of those “Alice showed me how to”- or “I picked this up from Bob”-style comments. They are the ones who introduced you do that game-changing editor feature, or taught you about dotfiles, or helped you get up to speed and build a good mental model for that part of the codebase. They recommended that book that you read that changed how you thought about something. They are the ones you frequently have deep, meaningful conversations with. Teammates share because they want to. Teammates make the people around them more effective now, while also helping them grow and expand their potential in the future. Here’s another way I see this happen. Suppose there is a task that requires some custom effort. Someone probably had to write a little script or something to do the job, maybe had to deal with some edge cases. Now, you are handed essentially that same ticket and a pointer that “it’s just like last time”. If you have a teammate, you would go to that other ticket, and likely find everything you need. They probably even shared the snippet they used. If you have a coworker, you would go that other ticket, and likely find nothing except it being marked “Done”. Coworkers aren’t interested in sharing. That’s not their job. Sure, the company isn’t a family . It’s not meant to be. But, as DHH says, You don’t have to pretend to be a family to be courteous. Or kind. Or protective. All those values can be expressed even better in principles, policies, and, most importantly, actions. Teammates care about the team. Teammates break down silos and artificial divisions, resulting in increased unity. They are people you enjoy working with, and you can tell they genuinely care about you. Their pattern of care often extends into their other communities as well. You sincerely look forward to interactions with teammates. Coworkers don’t bother investing in relationships outside what impacts their core job. Interactions with coworkers might be laborious and taxing. This one might be controversial. I think teammates enjoy the craftsmanship of whatever craft they are engaged in. They are often the people that inspire others from their enthusiasm. Maybe they play for the love of the game. Maybe they play to win. They are the ones metaphorically early to practice and always hitting the gym. Their enthusiasm and corresponding work ethic is contagious. Coworkers are just here to get that cash and do other things. Indeed, I think it is also common for a person to be a great teammate by this measure in some contexts while simultaneously not being a great teammate in others. (Nikola Jokić, who I’d argue is a great teammate, is one prominent example of an exception here with the way he exemplifies team play but, in the off-season, couldn’t care less about basketball.) I’m sure I’ve missed a lot of examples here, but even so, it’s been a good source of personal reflection to think about how I myself can try and be a good teammate. I certainly am convinced that striving to be a good teammate, and having good teammates, leads to improved job satisfaction and team success. A coworker sends a bare hello , a teammate includes the context necessary to make asynchronous communication effective. A teammate opens that PR fixing those 6 typos in the source code comments. A coworker sends that message tagging a bunch of people to investigate a “super flaky test”, only to discover the issue is a real bug and the tests were not flaking at all after people context switch to help. They do not respect others time as much as their own. A teammate is willing to context switch to help. A coworker opens a PR with a bunch of code that “might be used in the future”, even though it’s not right now, increasing maintenance burden. A teammate deletes dead code. A coworker provides unhelpful commit messages like “fix stuff”, or has poorly organized commits, or general doesn’t explain the why. A teammate provides thorough context that often helps future debugging efforts. Teammates introduce tooling, frameworks, design patterns, etc. to help systematically reduce complexity. A teammate is the type of person that puts their shopping carts away, regardless of whether people are watching.

1 views
Luke Hsiao 1 years ago

How to connect MTP to a kids profile on a Fire tablet

This is mostly a note-to-self. I’m a father with a kid that is starting to get old enough to appreciate some screen time. To that end, we got a cheap Amazon Fire tablet (sidenote, it seems Amazon has a corner on this market). It was nice, we set up a kid’s profile, disabled the store, disabled calling, disabled the web browser, shared VLC (for personal media) and AnkiDroid (for educational flashcards). But, then I couldn’t figure out for the life of me how to get media onto the internal storage in a way that was visible to that kid profile! Here’s the trick: Step 3 in particular was not obvious to me. Log in to the kid profile. Bring down the settings menu by dragging down on the top. Press and hold the Bluetooth icon until a PIN prompt appears. Enter your pin. The “Connected devices” menu will open, where you can change the USB setting to File transfer. Now, the internal storage reflects this kid profile specifically.

0 views