-
Aug 19, 2018
Tech debt
For a long time I’d considered technical debt to be just a bad thing. That it was a mess left behind by imprudent coders of the past. I hadn’t paused to reflect on the idea of it.
Only recently did I come to appreciate that tech debt can be a tool, just like monetary debt. Monetary debt is a (hopefully) temporary suboptimal state that’s intentionally entered with an eye toward a long-term goal. E.g., you take on debt to pay for a university degree that you think will result in you having a higher salary in the future. The debt is useful in this way. Tech debt, too, can be useful.
This became clear to me while working on a prototype to test market fit for a potential new feature at work. By making tradeoffs that sacrificed design robustness for speed of implementation, we intentionally introduced tech debt into the system. Designing and implementing a polished, scalable system before knowing whether the offering was valuable to our customers could have been an expensive mistake. Tech debt is allowing us to explore an idea and quickly react to evolving requirements.
Of course, just as with monetary debt, too much tech debt can be disastrous. Tech debt is also harder than monetary debt to measure. Still, it’s a useful tool in some scenarios, and I’m glad I have a more nuanced understanding of it.
-
Jun 20, 2018
Command Lining
I’ve slowly accumulated knowledge of a few tricks that help me to be productive at the terminal prompt. These are things that I somehow missed during years of computer science schooling and my early career. I often learn things, then immediately forget the time before I learned them, and then take that new information for granted. I’m trying to reflect on such learning and distill the best parts. And so hopefully someone who happens upon this list may find a new tip or two.
TLDR
TLDR - Simplified and community-driven man pages.
This isn’t a built-in bash command, but I’ve found it to be very handy. It’s one of the first programs I install on a new system. Type
tldr <command>
to see usage examples of the most common options of command line programs.Tree
Another not-built-in program. Imagine if
ls
recursively displayed a hierarchical directory listing. I like visual information, so I liketree
.tree -L 3 . └── uploads ├── 2013 │ ├── 11 │ └── 12 ├── 2014 │ ├── 03 │ ├── 06 │ ├── 07 ├── 2015 │ └── 01 ├── 2016 │ ├── 01 │ └── 04 ├── 2017 │ └── 07 └── 2018 ├── 01 └── 02
Return to previous directory
I happened to be reading an intro to bash commands once, and I was surprised that one of the first lessons showed a use of
cd
that I somehow wasn’t familiar with. This is how you can change the working directory back to the previous working directory. Great for hopping between a couple related projects.cd -
Curly brace expansion
This is a really cool feature of bash and one I wished I’d learned about earlier. Read more about it here.
for i in {0..4}; do echo $i; done 0 1 2 3 4
My favorite use of brace expansion is to expand two long path names. E.g.
cp ~/my/long/path/to/a/file.txt ~/my/long/path/to/a/file.txt.bak
can be rewritten as
cp ~/my/long/path/to/a/file.txt{,.bak}
Previous command
!!
will give you access to the previous command. Of course you can also press the up arrow, but this is useful for modifying the previous command. 99% of the time I use this it’s because I forgotsudo
.touch somefile.txt touch: somefile.txt: Permission denied sudo !! sudo touch somefile.txt
Search command history
Press ctrl + r to start searching backwards through the command history. This is another one I wish I’d learned sooner.
(reverse-i-search)`touc': touch somefile.txt
And the entire command history is available via
history
:history | tail -n3 13637 ls 13638 ll 13639 history | tail -n3
-
Jun 4, 2018
User Feedback
I spent some of my free time over the past three weeks rebuilding a small single page app I maintain: Tumblr Top (code here).
The original incarnation of this app was written circa 2015 in CoffeeScript and AngularJS 1.4. The new version is in React and Semantic UI. There are also a few simple charts to visualize blog post and tag popularity that were coded using the Victory charting library.
I rewrote the app as an excuse to use React on a personal project and to make the site easier to build and maintain going forward. But the original version was running smoothly with something like 2-3k users per month. I didn’t want to upset the regular users by fixing what wasn’t broken.
So I built out the updated site to what I thought was a reduced but perhaps satisfactory level of functionality. I hosted the new app at a different url, and added a callout to the old site asking users to try it out.
While the new app was in-progress, I invited the early adopters to leave comments and criticism. Google Forms made soliciting and collecting user feedback dead simple (alternatives: SurveyMonkey or Typeform). The feedback survey was to the point. It only had four questions:
- Do you like the redesign?
- Do you miss the [missing feature]?
- Were there any errors or bugs with the redesign? (If so, please provide your browser / operating system)
- Any other feedback about the redesign or the app in general?
Over the course of a couple weeks the survey received 19 responses. This was enough to help me massively improve the first cut. The answers to those four questions gave me a lot of insight:
- The performance improvements I’d added had come at the cost of my API access often being rate-limited
- There was a critical display bug in a browser I hadn’t tested
- Other small bugs were highlighted
- Users had become very accustomed to what I thought were unimportant design details of the original version
- A feature I thought was useless was meaningful to a large percentage of users
- Strangers on the internet are willing to fill out a survey
- You can’t please everybody
I’m thankful to the handful of power users who took time out of their lives to provide valuable feedback for the app. Without that survey I may have shipped a shoddy update and degraded my users’ experience.
-
Apr 12, 2018
Spreadsheet Jeopardy!
My girlfriend and I often watch Jeopardy! together, and we play along and keep score to make it more fun.
To avoid having to pause the show to wager, we have a house rule that “Daily Double”s are just worth twice their marked value. E.g., a D.D. on a $600 clue would be worth $1200. And we yell the answers (questions?) instead of buzzing in.
After tallying a number of games by hand, I decided to make a custom function in Google Sheets to keep score. Our scoring notation is:
- correct: first initial of first name
- incorrect: first initial of last name
- daily double: initial followed by asterisk
function score(results, level) { mScore = 0; kScore = 0; results[0].forEach( function(cell) { cell = cell.toLowerCase() if (cell.match(/.*m\*.*/)) { mScore += 2 * level; } else if (cell.match(/.*m.*/) !== null) { mScore += level; } else if (cell.match(/.*b\*.*/)) { mScore -= 2 * level; } else if (cell.match(/.*b.*/)) { mScore -= level; } if (cell.match(/.*k\*.*/)) { kScore += 2 * level; } else if (cell.match(/.*k.*/)) { kScore += level; } else if (cell.match(/.*c\*.*/)) { kScore -= 2 * level; } else if (cell.match(/.*c.*/)) { kScore -= level; } }) return [[mScore, kScore]]; }
(Yes, I’m aware this could be refactored)
It just occurred to me that I could probably modify this function to gift me a stray hundred dollars every now and then. She probably wouldn’t notice… 😈
-
Apr 9, 2018
Setting Emacs Theme Based on Ambient Light
I sit next to a window at work. On sunny days it’s easier to see a light editor theme, and when the sky is dark, a dark theme is easier on my eyes. So I decided to try to have my MacBook automatically switch the Emacs theme based on readings from the ambient light sensor.
There are two parts to this solution: a command-line executable to read data from the sensor, and then a small elisp function to do the theme switching.
The program below is from StackOverflow and slightly modified. It gets the
AppleLMUController
IO service, then, when the service is ready, prints the light sensor data to stdout and exits.// lmutracker.mm // // clang -o lmutracker lmutracker.mm -framework IOKit -framework CoreFoundation #include <mach/mach.h> #import <IOKit/IOKitLib.h> #import <CoreFoundation/CoreFoundation.h> static double updateInterval = 0.1; static io_connect_t dataPort = 0; void updateTimerCallBack(CFRunLoopTimerRef timer, void *info) { kern_return_t kr; uint32_t outputs = 2; uint64_t values[outputs]; kr = IOConnectCallMethod(dataPort, 0, nil, 0, nil, 0, values, &outputs, nil, 0); if (kr == KERN_SUCCESS) { printf("%8lld", values[0]); exit(0); } if (kr == kIOReturnBusy) { return; } mach_error("I/O Kit error:", kr); exit(kr); } int main(void) { kern_return_t kr; io_service_t serviceObject; CFRunLoopTimerRef updateTimer; serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleLMUController")); if (!serviceObject) { fprintf(stderr, "failed to find ambient light sensors\n"); exit(1); } kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &dataPort); IOObjectRelease(serviceObject); if (kr != KERN_SUCCESS) { mach_error("IOServiceOpen:", kr); exit(kr); } setbuf(stdout, NULL); updateTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + updateInterval, updateInterval, 0, 0, updateTimerCallBack, NULL); CFRunLoopAddTimer(CFRunLoopGetCurrent(), updateTimer, kCFRunLoopDefaultMode); CFRunLoopRun(); exit(0); }
The accompanying elisp code will invoke that executable on a timer and change the theme based on the light reading.
(setq current-theme "dark") (defconst light-theme 'majapahit-light) (defconst dark-theme 'majapahit-dark) ;; will apply a dark theme if the room is dark, and a light theme if the room is ;; bright (defun change-theme-for-lighting () (let* ((current-light-sensor-reading (string-to-number (shell-command-to-string "./lmutracker")))) (if (< current-light-sensor-reading 100000) (when (not (string-equal current-theme "dark")) (load-theme dark-theme 1) (setq current-theme "dark")) (when (not (string-equal current-theme "light")) (load-theme light-theme 1) (setq current-theme "light"))))) ;; probably want to run this less frequently than every second (run-with-timer 0 1 #'change-theme-for-lighting)
Edit 4/7/2019: Read a Russian translation of this post here.
Recent Posts
- Flight Focus
- Generative AI Accelerates Our Exploration of the Search Space
- AI Assistant Use-Case: Performance Feedback
- Poor Man's Computer Use with Execute Arbitrary AppleScript MCP Server
- Please don’t disable paste
- Blogging via Email
- Using an E-Ink Monitor: Part 2
- AI and the Uncertain Future of Work
- Using an E-Ink Monitor
- Rendering ChatGPT HTML Output Inline