<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Grant Isom — Blog</title><description>Apps, AI skills, and writing by Grant Isom, a software engineer building AI agents in Kansas City.</description><link>https://grantisom.com/</link><language>en-us</language><item><title>Vampire: Keep Your MacBook Awake</title><link>https://grantisom.com/2026/09/01/vampire.html</link><guid isPermaLink="true">https://grantisom.com/2026/09/01/vampire.html</guid><description>I built a menu bar app to keep my MacBook awake with the lid closed, then made sure it could reliably restore normal sleep.</description><pubDate>Tue, 01 Sep 2026 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I close my MacBook a lot while it&apos;s still doing something. Long build, big download, whatever. Lid down, walk away. macOS sleeps on lid close, and the fix is one command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo pmset -a disablesleep 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s the whole feature. I&apos;ve been typing it for years.&lt;/p&gt;
&lt;p&gt;So I built &lt;a href=&quot;https://github.com/glisom/vampire&quot;&gt;Vampire&lt;/a&gt;, a menu bar app that runs it, and then spent basically all of the effort on the other command.&lt;/p&gt;
&lt;h2&gt;The two ways I kept failing&lt;/h2&gt;
&lt;p&gt;The first is just typing it. Works instantly, and nothing tracks it. Then I forget, because the machine looks completely normal, and three days later I close the lid, drop the laptop in a bag, and now it&apos;s a space heater with a zipper. The setting is global, sticky, and survives reboots. Nothing on screen tells you it&apos;s on.&lt;/p&gt;
&lt;p&gt;The second is wrapping it. An alias, a Shortcut, an AppleScript with a privilege prompt. Now I get an admin password dialog every single time I want to flip a boolean, which is enough friction that I go right back to the terminal.&lt;/p&gt;
&lt;p&gt;Worth saying since I checked first: Caffeine, Amphetamine, and Lungo don&apos;t do this. They take I/O Kit power assertions, which stop idle sleep and display sleep. Lid close is a separate mechanism, and &lt;code&gt;caffeinate&lt;/code&gt; doesn&apos;t touch it either. Lungo is great at what it actually does, and Vampire isn&apos;t trying to replace it.&lt;/p&gt;
&lt;h2&gt;The feature is the toggle, the product is the undo&lt;/h2&gt;
&lt;p&gt;Once you decide an app is going to hold this setting for you, the real question is what happens when the app isn&apos;t there anymore. Every one of those paths has to land back at &lt;code&gt;disablesleep 0&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;th&gt;Who restores Off&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Normal quit&lt;/td&gt;
&lt;td&gt;The app&lt;/td&gt;
&lt;td&gt;Requests Off, waits for the helper to acknowledge, exits only then&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crash or Force Quit&lt;/td&gt;
&lt;td&gt;The helper&lt;/td&gt;
&lt;td&gt;XPC invalidation fires, and if the marker exists it restores Off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Helper crash&lt;/td&gt;
&lt;td&gt;The helper&lt;/td&gt;
&lt;td&gt;Every helper launch normalizes to Off before the listener accepts a connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mac restart&lt;/td&gt;
&lt;td&gt;The helper&lt;/td&gt;
&lt;td&gt;Same startup path, run by launchd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore fails&lt;/td&gt;
&lt;td&gt;Nobody, yet&lt;/td&gt;
&lt;td&gt;The marker stays, state stays Error, and recovery retries on every future start&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That last row is the one I care about most. The tempting move when a restore fails is to drop the marker anyway and show Off, because error states are ugly. But then the app is asserting something about your hardware that isn&apos;t true. Vampire never claims Off while recovery is unresolved. It would rather show you a warning triangle it can actually back up.&lt;/p&gt;
&lt;h2&gt;Write the intent before you change the world&lt;/h2&gt;
&lt;p&gt;The recovery marker is a root-owned plist at &lt;code&gt;/Library/Application Support/Insomnia/active.plist&lt;/code&gt;, and the ordering around it is the load-bearing part:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;On:&lt;/strong&gt; create the marker, &lt;em&gt;then&lt;/em&gt; run pmset.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Off:&lt;/strong&gt; run pmset, &lt;em&gt;then&lt;/em&gt; remove the marker.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both directions record the risky state first. If you flip the setting and crash before writing anything down, nothing knows there&apos;s something to undo, and the next boot leaves you with a laptop that quietly can&apos;t sleep. Erring toward a stale marker is cheap, since a stale marker just means the next startup runs a &lt;code&gt;disablesleep 0&lt;/code&gt; that was already 0. Erring the other way means a silently broken machine.&lt;/p&gt;
&lt;h2&gt;Never show a checkmark you can&apos;t defend&lt;/h2&gt;
&lt;p&gt;pmset exiting 0 doesn&apos;t mean the setting took. So the helper writes, then reads back &lt;code&gt;pmset -g custom&lt;/code&gt; and requires every reported power profile to equal what it asked for. Disagreement between profiles is a failure, not a tiebreaker.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;disablesleep&lt;/code&gt; is undocumented, though, and macOS sometimes just omits the key. In that case the successful write is accepted only on hardware that should support it, and the release checklist covers the rest with a physical lid close.&lt;/p&gt;
&lt;p&gt;The menu item sits downstream of all of that. Clicking it doesn&apos;t check the box. The box gets checked when the helper says the change happened and verification passed.&lt;/p&gt;
&lt;h2&gt;The privileged part stays boring&lt;/h2&gt;
&lt;p&gt;The helper runs as root, so the interesting work is in how little it&apos;s allowed to do.&lt;/p&gt;
&lt;p&gt;It executes exactly three commands, all with fixed arguments, none of which come from the caller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;/usr/bin/pmset -a disablesleep 1
/usr/bin/pmset -a disablesleep 0
/usr/bin/pmset -g custom
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s no shell anywhere in that path (&lt;code&gt;Process&lt;/code&gt; straight to the executable URL, never &lt;code&gt;/bin/sh&lt;/code&gt;), and the XPC interface has no generic execute method to abuse. It exposes three operations: status, on, off.&lt;/p&gt;
&lt;p&gt;Before the listener activates, the helper reads its own Team ID out of the Security framework and builds a code signing requirement from that verified runtime identity, then hands it to &lt;code&gt;setConnectionCodeSigningRequirement&lt;/code&gt;. A caller has to be bundle ID &lt;code&gt;co.groundwork-ai.insomnia&lt;/code&gt;, signed by the same team. macOS rejects everything else before my delegate ever runs, which is my favorite kind of security code, the kind you don&apos;t write.&lt;/p&gt;
&lt;p&gt;And because &quot;the helper must never invoke a shell&quot; is a sentence in a doc, and sentences don&apos;t fail builds, CI greps for it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rg -n &apos;/bin/(sh|bash|zsh)|system\(|popen\(&apos; InsomniaHelper/Sources
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A hit fails the build. The same script fails on &lt;code&gt;URLSession&lt;/code&gt;, &lt;code&gt;Sparkle&lt;/code&gt;, &lt;code&gt;Telemetry&lt;/code&gt;, or &lt;code&gt;Analytics&lt;/code&gt; in runtime source, which is how &quot;no network, no updater, no telemetry&quot; stays true instead of staying aspirational.&lt;/p&gt;
&lt;h2&gt;Small things I liked&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Hardware support without a whitelist.&lt;/strong&gt; Model whitelists rot. Vampire supports anything whose model identifier starts with &lt;code&gt;MacBook&lt;/code&gt;, plus anything starting with &lt;code&gt;Mac&lt;/code&gt; where I/O Kit reports an internal battery. Covers old and new naming, excludes desktops, and doesn&apos;t get fooled by a UPS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The repo says vampire, the code says Insomnia.&lt;/strong&gt; Bundle ID, helper label, Mach service, and recovery path all kept their original names on purpose. Renaming the bundle ID would invalidate the helper approval the user already granted, and the signed XPC contract with it. A rename there is a user-visible change, not a cosmetic one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A target whose job is to fail.&lt;/strong&gt; &lt;code&gt;InsomniaWrongIdentifierClient&lt;/code&gt; is a binary whose entire source is &lt;code&gt;exit(EXIT_SUCCESS)&lt;/code&gt;. It exists so an integration test can point the real signing requirement at a wrong-team binary and assert it gets rejected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;76 tests, and about as many lines of test as source&lt;/strong&gt; (1,547 to 1,549, which I promise wasn&apos;t on purpose). They all use a fake command runner and a temporary marker store, so &lt;code&gt;scripts/ci.sh&lt;/code&gt; never touches the real power settings of the machine running it. Anything involving real pmset, real registration, or a real lid close is a checklist item with an explicit ask, and it always restores afterward.&lt;/p&gt;
&lt;h2&gt;What it isn&apos;t&lt;/h2&gt;
&lt;p&gt;Vampire changes lid-close sleep only. It doesn&apos;t prevent idle sleep, display sleep, or screen lock. No timers, schedules, shortcuts, or automatic activation. No network, analytics, updater, Dock icon, or window. MacBooks with a built-in lid, macOS 13 Ventura or newer.&lt;/p&gt;
&lt;p&gt;The honest limitation: verification leans on an undocumented pmset key. When macOS reports it, Vampire is strict about it. When macOS doesn&apos;t, the best it can do is trust exit status on hardware that should support the setting. That gap is a big part of why it never enables itself at login, and why quitting always restores.&lt;/p&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;p&gt;Grab &lt;code&gt;Vampire.dmg&lt;/code&gt; from &lt;a href=&quot;https://github.com/glisom/vampire/releases/latest&quot;&gt;Releases&lt;/a&gt; and check it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;shasum -a 256 -c Vampire.dmg.sha256
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Drag it to Applications, launch it, approve the helper once. After that, On and Off never ask for a password again.&lt;/p&gt;
&lt;p&gt;Building from source needs Xcode 26 and XcodeGen:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;xcodegen generate
scripts/ci.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Signed, notarized, MIT, and no runtime dependencies outside Apple&apos;s own frameworks. Code&apos;s &lt;a href=&quot;https://github.com/glisom/vampire&quot;&gt;on GitHub&lt;/a&gt;. If it saves your laptop from cooking in a backpack even once, that&apos;s the whole return on investment!&lt;/p&gt;
</content:encoded></item><item><title>skill-thief: Steal the Ideas, Not the Install</title><link>https://grantisom.com/2026/09/01/skill-thief.html</link><guid isPermaLink="true">https://grantisom.com/2026/09/01/skill-thief.html</guid><description>A Claude Code plugin that reads someone else&apos;s agent tooling, pulls out the mechanisms worth having, and decides where each one actually belongs in your setup.</description><pubDate>Tue, 01 Sep 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I read a lot of other people&apos;s agent setups. Skills repos, plugins, conference talks, that one blog post everybody links. There are almost always good ideas in there. What I kept getting wrong was what to do next.&lt;/p&gt;
&lt;p&gt;So I built &lt;a href=&quot;https://github.com/glisom/skill-thief&quot;&gt;skill-thief&lt;/a&gt;, a Claude Code plugin that runs the in-between step.&lt;/p&gt;
&lt;h2&gt;The two ways I kept failing&lt;/h2&gt;
&lt;p&gt;The first one is installing the whole thing. It feels like the fast path! Add the marketplace, enable the plugin, move on. Except now every skill in it loads into every session from then on, and all those descriptions compete for routing with everything I already had. I paid that cost forever, and the actual insight was three paragraphs.&lt;/p&gt;
&lt;p&gt;The second one is doing it by hand. I skim the source, grab the two ideas I like, apply them, and lose the reasoning behind everything else. Six months later I read the same repo from scratch and re-derive the same conclusions. Worse, I re-propose the ideas I already looked at and deliberately passed on, because nothing wrote down that I passed on them or why.&lt;/p&gt;
&lt;p&gt;skill-thief is the repeatable thing in the middle.&lt;/p&gt;
&lt;h2&gt;Every idea gets exactly one verdict&lt;/h2&gt;
&lt;p&gt;The whole plugin is built around a four-item ladder. Each mechanism it pulls out of a source lands in exactly one box:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;th&gt;Applies when&lt;/th&gt;
&lt;th&gt;Lands in&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Absorb as a rule&lt;/strong&gt; (default)&lt;/td&gt;
&lt;td&gt;No independent trigger. Nobody would ever invoke this by name.&lt;/td&gt;
&lt;td&gt;An existing skill body, your agent instruction file, or a conventions doc&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mint a new skill&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;It has its own trigger. Someone would ask for this at a moment when nothing else would fire.&lt;/td&gt;
&lt;td&gt;A new skill directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Harden a gate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;It&apos;s a check, not prose. The value is that it fails a build.&lt;/td&gt;
&lt;td&gt;A validation script or test tier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reject&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;There&apos;s a durable, concept-level reason not to adopt it.&lt;/td&gt;
&lt;td&gt;The rejection record&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The important part is that &quot;absorb as a rule&quot; is the default, not the neutral middle. Minting a skill is the expensive verdict. It spends description budget in every future session and competes for routing with everything already installed. Two overlapping triggers don&apos;t average out into good coverage, they just make the router&apos;s job harder for both. So a mechanism has to earn &quot;new skill&quot; by showing its own trigger, not by being useful.&lt;/p&gt;
&lt;p&gt;Reject has two guardrails I care about a lot. A deferral is not a rejection (&quot;we&apos;re busy this quarter&quot; changes with the calendar, so it doesn&apos;t go in the permanent record). And you never file an already-implemented idea as rejected, because a future reader will conclude you tried it and it failed, and miss that it&apos;s live and working.&lt;/p&gt;
&lt;h2&gt;Mechanisms, not features&lt;/h2&gt;
&lt;p&gt;The other rule that does real work: pull out the mechanism, not the feature.&lt;/p&gt;
&lt;p&gt;Say a source ships something called AutoReviewGPT that scores a PR against a five-item checklist and blocks the merge under a threshold. The feature name is AutoReviewGPT. The mechanism is &quot;a weighted rubric gate that blocks on a numeric pass threshold before merge.&quot; You write down the second one.&lt;/p&gt;
&lt;p&gt;The litmus test is simple: strip the source&apos;s name and product out entirely. Does what you wrote still tell you what to build? If not, you copied a feature. A mechanism carried over under someone else&apos;s marketing name shows up with their assumptions attached, and those get a lot harder to spot once the name sticks.&lt;/p&gt;
&lt;h2&gt;How a run goes&lt;/h2&gt;
&lt;p&gt;Six phases, and only one place where anything gets written.&lt;/p&gt;
&lt;p&gt;It starts by inventorying &lt;em&gt;your&lt;/em&gt; repo and building a map of where each of the four verdicts would even land here. That map gets shown before it reads the source, so a bad guess is cheap to fix. Then it pins the exact version it&apos;s reading (a commit SHA, a publish date, a URL), scouts the source for mechanisms, and greps your repo for prior art.&lt;/p&gt;
&lt;p&gt;That prior-art phase is my favorite bit, honestly. Evidence of absence has to be actual evidence: the literal command and its actual output, including the empty result. &quot;I didn&apos;t see it&quot; and &quot;I searched these six paths with these three patterns and got nothing&quot; are different claims, and only the second one holds up a verdict.&lt;/p&gt;
&lt;p&gt;Then everything lands in one ranked table, at one approval gate, once. Not a finding-by-finding approval loop, because that defeats the point of ranking them. Nothing is written before that gate and everything is written after it.&lt;/p&gt;
&lt;p&gt;Re-run it later against the same source and it goes into diff mode. It reads the findings doc from last time, resolves the current version, and reports only what changed.&lt;/p&gt;
&lt;h2&gt;It&apos;s not an installer&lt;/h2&gt;
&lt;p&gt;Worth being loud about: skill-thief never adds a marketplace, enables someone else&apos;s plugin, or vendors their code. The only output is changes to your own config. It also doesn&apos;t write feature code, and it isn&apos;t an auditor for what you already have installed (different question, different cadence).&lt;/p&gt;
&lt;p&gt;One real limitation. A thin host repo weakens the prior-art check. If there&apos;s not much of your own tooling to find, every mechanism looks like a gap and it will over-recommend. Verdict quality tracks how much of your conventions actually live in files instead of in people&apos;s heads.&lt;/p&gt;
&lt;p&gt;I ran it against &lt;a href=&quot;https://github.com/obra/superpowers&quot;&gt;obra/superpowers&lt;/a&gt; as a dry run and it&apos;s checked into the repo under &lt;code&gt;docs/examples/&lt;/code&gt;. Eight mechanisms scouted, three made the gate table (one of each verdict, as it happens). Three of the other five got filed as &quot;you already do this better,&quot; and two as &quot;doesn&apos;t apply at your size yet.&quot;&lt;/p&gt;
&lt;p&gt;That last bucket is there because of the dry run. In the first pass those two got labeled Reject, and an independent review caught that both reasons were really about how small the repo is, not about the mechanisms. That&apos;s a deferral wearing a rejection&apos;s clothes, which the guardrail was supposed to catch and didn&apos;t. So the ladder got a new category out of it. Pretty good outcome for a dry run!&lt;/p&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;/plugin marketplace add glisom/skill-thief
/plugin install skill-thief
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then point it somewhere:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Use skill-thief on https://github.com/example/agent-skills and tell me
what&apos;s worth taking.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not on Claude Code? The skill body is written harness-neutral on purpose, so copying &lt;code&gt;skills/skill-thief/&lt;/code&gt; into your agent&apos;s skills directory works the same way. There&apos;s no second copy to keep in sync.&lt;/p&gt;
&lt;p&gt;Zero dependencies, MIT, Node 20+. Code&apos;s &lt;a href=&quot;https://github.com/glisom/skill-thief&quot;&gt;on GitHub&lt;/a&gt;. If you run it against something interesting, I&apos;d love to hear what it told you to steal.&lt;/p&gt;
</content:encoded></item><item><title>Bringing ListWithMe Back to Life</title><link>https://grantisom.com/2026/02/24/listwithme-returns.html</link><guid isPermaLink="true">https://grantisom.com/2026/02/24/listwithme-returns.html</guid><description>After years off the App Store, I&apos;ve completely rebuilt ListWithMe from the ground up with SwiftUI and modern iOS features.</description><pubDate>Tue, 24 Feb 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Back in 2019, I built &lt;a href=&quot;https://grantisom.com/listwithme/&quot;&gt;ListWithMe&lt;/a&gt;—a simple iMessage app for creating shared shopping lists. My wife and I used it constantly for grocery runs and weekend errands. It was nothing fancy, but it solved a real problem: quickly creating a list that both of us could see and edit in real-time, right inside our conversation.&lt;/p&gt;
&lt;p&gt;Then life happened. The app sat untouched as iOS evolved, and eventually Apple removed it from the App Store. I always meant to update it, but between work and other projects, it kept getting pushed to &quot;someday.&quot;&lt;/p&gt;
&lt;p&gt;Well, someday finally arrived.&lt;/p&gt;
&lt;h2&gt;A Complete Rebuild&lt;/h2&gt;
&lt;p&gt;Rather than patch the old UIKit codebase, I decided to rebuild ListWithMe from scratch using SwiftUI. The original app was functional but minimal—just lists and items. The new version keeps that simplicity while adding features I&apos;ve wanted for years:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Smart Input&lt;/strong&gt; — Type &quot;3 apples&quot; or &quot;milk x2&quot; and the app automatically parses the quantity. It sounds small, but it makes adding items so much faster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Priorities &amp;amp; Due Dates&lt;/strong&gt; — Not everything on a shopping list is equal. Now you can mark items as high priority or set a due date for time-sensitive errands.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Categories&lt;/strong&gt; — Group items by aisle or type. When you&apos;re navigating a store, having items organized by Produce, Dairy, and Frozen makes the trip faster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Activity Feed&lt;/strong&gt; — See who added or completed items. When you&apos;re shopping together but apart, it&apos;s helpful to know what your partner just grabbed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Batch Actions&lt;/strong&gt; — Select multiple items to complete or delete at once. Clear all completed items with a single tap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Search &amp;amp; Sort&lt;/strong&gt; — Find items quickly in long lists. Sort by name, category, priority, due date, or drag to reorder manually.&lt;/p&gt;
&lt;h2&gt;The Technical Side&lt;/h2&gt;
&lt;p&gt;The rebuild gave me a chance to work with some iOS technologies I hadn&apos;t used in personal projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SwiftUI&lt;/strong&gt; with the new &lt;code&gt;@Observable&lt;/code&gt; macro (iOS 17+)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Core Data&lt;/strong&gt; with CloudKit sync for seamless multi-device support&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modern Swift&lt;/strong&gt; patterns and concurrency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The codebase is &lt;a href=&quot;https://github.com/glisom/ListWithMe&quot;&gt;open source on GitHub&lt;/a&gt; if you want to poke around. It&apos;s a good example of a relatively simple but complete SwiftUI app with persistence.&lt;/p&gt;
&lt;h2&gt;Why iMessage Apps Still Matter&lt;/h2&gt;
&lt;p&gt;When I first released ListWithMe, the biggest complaint was discoverability. Users would download it, then struggle to find where it went—because iMessage apps don&apos;t appear on your home screen. That&apos;s still true today, and it&apos;s still the biggest friction point.&lt;/p&gt;
&lt;p&gt;But I think there&apos;s something valuable about apps that live inside your conversations. You don&apos;t need to context-switch. You don&apos;t need to share links or coordinate which app to use. If you&apos;re already texting someone, the list is right there.&lt;/p&gt;
&lt;p&gt;For my wife and me, it&apos;s become invisible infrastructure. We don&apos;t think about it—we just have a shared list that updates as we shop.&lt;/p&gt;
&lt;h2&gt;What&apos;s Next&lt;/h2&gt;
&lt;p&gt;ListWithMe 2.0 is available now on the &lt;a href=&quot;https://apps.apple.com/us/app/listwithme/id1224284271&quot;&gt;App Store&lt;/a&gt;. I&apos;m planning to keep iterating based on feedback. A few things I&apos;m considering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recurring lists (weekly grocery templates)&lt;/li&gt;
&lt;li&gt;Siri integration&lt;/li&gt;
&lt;li&gt;Widgets for quick access&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you try it out, I&apos;d love to hear what you think. You can find me on &lt;a href=&quot;https://github.com/glisom&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&apos;s to shared lists and fewer forgotten items.&lt;/p&gt;
</content:encoded></item><item><title>HealthQL Now Supports React Native</title><link>https://grantisom.com/2026/02/07/healthql-react-native.html</link><guid isPermaLink="true">https://grantisom.com/2026/02/07/healthql-react-native.html</guid><description>HealthQL v1.1.0 brings full React Native and Expo support—query Apple HealthKit using SQL from your JavaScript apps...</description><pubDate>Sat, 07 Feb 2026 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;HealthQL v1.1.0 adds full React Native and Expo support. You can now query Apple HealthKit using the same SQL syntax from JavaScript/TypeScript apps.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/glisom/HealthQL&quot;&gt;GitHub&lt;/a&gt; | &lt;a href=&quot;https://glisom.github.io/HealthQL&quot;&gt;Documentation&lt;/a&gt; | &lt;a href=&quot;https://www.npmjs.com/package/react-native-healthql&quot;&gt;npm&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Quick Example&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { HealthQL } from &apos;react-native-healthql&apos;;

// Request authorization
await HealthQL.requestAuthorization({
  read: [&apos;heart_rate&apos;, &apos;steps&apos;, &apos;sleep_analysis&apos;],
});

// Query with SQL
const results = await HealthQL.query(`
  SELECT avg(value) FROM heart_rate
  WHERE date &amp;gt; today() - 7d
  GROUP BY day
`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same SQL syntax as the Swift version—the React Native package wraps the native Swift implementation, so you get real HealthKit queries, not mock data.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install react-native-healthql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the Expo config plugin to your &lt;code&gt;app.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;expo&quot;: {
    &quot;plugins&quot;: [
      [&quot;react-native-healthql&quot;, {
        &quot;healthShareUsageDescription&quot;: &quot;Read health data to display insights&quot;
      }]
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then rebuild:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx expo prebuild --clean
npx expo run:ios
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;What&apos;s Included&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Full TypeScript types&lt;/li&gt;
&lt;li&gt;All 18 quantity types (heart rate, steps, calories, etc.)&lt;/li&gt;
&lt;li&gt;Category types (sleep analysis, headache, fatigue)&lt;/li&gt;
&lt;li&gt;Workouts and sleep sessions&lt;/li&gt;
&lt;li&gt;Schema introspection for building dynamic UIs&lt;/li&gt;
&lt;li&gt;Structured error handling with suggestions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Also New: CocoaPods Support&lt;/h3&gt;
&lt;p&gt;The Swift library is now available via CocoaPods in addition to SPM:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;pod &apos;HealthQL&apos;, &apos;~&amp;gt; 1.1.0&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Platform Support&lt;/h3&gt;
&lt;p&gt;iOS only for now. Android throws a clear &lt;code&gt;PLATFORM_NOT_SUPPORTED&lt;/code&gt; error—Health Connect support is on the roadmap.&lt;/p&gt;
&lt;p&gt;Check the &lt;a href=&quot;https://glisom.github.io/HealthQL/#/react-native&quot;&gt;React Native docs&lt;/a&gt; for the full API reference.&lt;/p&gt;
</content:encoded></item><item><title>HealthQL: SQL for Apple HealthKit</title><link>https://grantisom.com/2026/02/01/healthql-sql-for-healthkit.html</link><guid isPermaLink="true">https://grantisom.com/2026/02/01/healthql-sql-for-healthkit.html</guid><description>I built a Swift library that lets you query HealthKit data using SQL syntax...</description><pubDate>Sun, 01 Feb 2026 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I built a Swift library that lets you query HealthKit data using SQL syntax. Working with HealthKit directly involves a lot of boilerplate—callback-based APIs, type-specific query objects, manual result transformation. HealthQL simplifies this into something more familiar.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/glisom/HealthQL&quot;&gt;GitHub&lt;/a&gt; | &lt;a href=&quot;https://glisom.github.io/HealthQL&quot;&gt;Documentation&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Quick Example&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT avg(value), min(value), max(value)
FROM heart_rate
WHERE date &amp;gt; today() - 7d
GROUP BY day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returns daily heart rate stats for the past week. The library handles all the HealthKit query setup, permissions, and result formatting.&lt;/p&gt;
&lt;h3&gt;Swift DSL&lt;/h3&gt;
&lt;p&gt;If you prefer type-safety, there&apos;s also a Swift DSL:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-swift&quot;&gt;let result = try await Health
    .select(.heartRate, aggregates: [.avg, .min, .max])
    .where(.date, .greaterThan, .date(.daysAgo(7)))
    .groupBy(.day)
    .execute()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both approaches compile down to the same intermediate representation before hitting HealthKit.&lt;/p&gt;
&lt;h3&gt;Supported Data&lt;/h3&gt;
&lt;p&gt;The library covers most common health types—steps, heart rate, calories, workouts, sleep sessions, and more. It supports aggregations, grouping by time periods, and the standard comparison operators you&apos;d expect.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;Add it via Swift Package Manager:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://github.com/glisom/HealthQL.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://glisom.github.io/HealthQL&quot;&gt;documentation site&lt;/a&gt; has the full syntax reference and more examples. Feel free to open an issue if you run into any problems or have suggestions.&lt;/p&gt;
</content:encoded></item><item><title>Accessibility Testing in Maestro</title><link>https://grantisom.com/2023/07/19/accessibility-testing-in.html</link><guid isPermaLink="true">https://grantisom.com/2023/07/19/accessibility-testing-in.html</guid><description>Recently, our mobile team came across a problem with text sizing in our app when customers..</description><pubDate>Wed, 19 Jul 2023 15:33:47 GMT</pubDate><content:encoded>&lt;p&gt;Recently, our mobile team came across a problem with text sizing in our app when customers had the &quot;Larger Accessibility Sizes&quot; Display Text option on. After updating our app to more fully support dynamic text, we came across a cool solution to automate testing this for future releases.&lt;/p&gt;
&lt;p&gt;Using &lt;a href=&quot;https://maestro.mobile.dev&quot;&gt;Maestro&lt;/a&gt; UI Testing, we could enable these settings for certain tests. We created actions within our flows to enable certain settings prior to running the actual UI tests steps. This pattern can be applied on both Android or iOS as Maestro gives you full access to the simulator even running in their cloud environment. If you have not checked out Maestro for UI testing on either React Native or Native applications, I would highly recommend it, as it solves so many historical issues with similar tools.&lt;/p&gt;
&lt;p&gt;Below is an example enabling the &quot;Larger Accessibility Sizes&quot; and then launching the target application in an iOS Simulator.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example Maestro Flow&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;appId: &amp;lt;your_bundle_id&amp;gt;
---
- tapOn:
	id: &quot;Settings&quot;
- tapOn:
	id: &quot;ACCESSIBILITY&quot;
- tapOn:
	id: &quot;DISPLAY_AND_TEXT&quot;
- tapOn:
	id: &quot;LARGER_TEXT&quot;
- tapOn: text: &quot;Larger Accessibility Sizes&quot;
	index: 0
- tapOn:
	point: &quot;50%,90%&quot;
- pressKey: &quot;Home&quot;
- launchApp:
	appId: &quot;&amp;lt;your_bundle_id&amp;gt;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This pattern or strategy can be applied to any of the accessibility settings available on simulators/emulators. This can be a huge help to ensure new UI updates and features are accessible while continuing to release fast.&lt;/p&gt;
</content:encoded></item><item><title>Using Act to Run Github Actions Locally</title><link>https://grantisom.com/2023/05/15/using-act-to.html</link><guid isPermaLink="true">https://grantisom.com/2023/05/15/using-act-to.html</guid><description>The Act package offers a convenient way to execute your GitHub Action workflows locally...</description><pubDate>Mon, 15 May 2023 22:06:58 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/nektos/act&quot;&gt;https://github.com/nektos/act&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Act package offers a convenient way to execute your GitHub Action workflows locally. It&apos;s a valuable tool when debugging or working on issues that either have a long runtime or are complex, saving you those precious minutes.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;The installation process is quite straightforward. First, ensure Docker Desktop is installed on your computer. Then, for Mac/Linux users, the easiest installation method is via &lt;a href=&quot;https://brew.sh&quot; title=&quot;Homebrew&quot;&gt;Homebrew&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;brew install act
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s all there is to it! Upon executing your workflows for the first time, the required Docker containers should automatically download.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;An essential point to remember is to &lt;a href=&quot;https://docs.github.com/en/enterprise-server@3.4/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token&quot;&gt;pass in your personal access token from GitHub&lt;/a&gt; when running any workflow dependent on SECRETS or environment variables.&lt;/p&gt;
&lt;h3&gt;Executing Workflows&lt;/h3&gt;
&lt;p&gt;With everything set up, running workflows becomes a breeze:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;➜ act pull_request --container-architecture linux/amd64
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Checkout the README for additional examples and a comprehensive breakdown of all the capabilities this package offers. I believe Act is an indispensable tool for anyone working with GitHub Actions who wants to test or debug their changes swiftly.&lt;/p&gt;
</content:encoded></item><item><title>Expo App Config Setup for Multiple Environments</title><link>https://grantisom.com/2023/02/01/expo-app-config.html</link><guid isPermaLink="true">https://grantisom.com/2023/02/01/expo-app-config.html</guid><description>Expo is a free and open-source platform for creating and building mobile apps using...</description><pubDate>Wed, 01 Feb 2023 04:22:25 GMT</pubDate><content:encoded>&lt;p&gt;Expo is a free and open-source platform for creating and building mobile apps using JavaScript and React Native. It is a popular choice among developers for its ease of use and its ability to quickly set up and run a mobile app without the need for extensive configuration. Expo provides a set of tools and services that allow developers to build, test, and publish their apps to the App Store and Google Play with minimal setup and without the need for Xcode or Android Studio.&lt;/p&gt;
&lt;p&gt;One of the most powerful features of Expo is the ability to use a single codebase to create apps for both iOS and Android platforms, which can save a lot of time and effort. Expo also provides a rich set of pre-built components and APIs for common functionality such as push notifications, camera access, and location tracking, which can help developers get their apps up and running quickly.&lt;/p&gt;
&lt;p&gt;One of the things you may come across is figuring out how to setup different environments or app variants within an Expo app. If you want to just target one binary on iOS and Android, there is very little to do other than setup some &lt;a href=&quot;https://docs.expo.dev/guides/environment-variables/&quot;&gt;environment variables&lt;/a&gt;. If you want to have multiple apps for development, QA/test, production, etc then you’ll need to do a few things to be able to take advantage of all Expo has to offer without having to manage these things yourself.&lt;/p&gt;
&lt;h2&gt;Converting to app.json to app.config.js for multiple environments.&lt;/h2&gt;
&lt;p&gt;The first step is to convert the &lt;code&gt;app.json&lt;/code&gt; file into an &lt;code&gt;app.config.js&lt;/code&gt; file. This allows for dynamic changes for things such as your app’s bundle identifier/package name. Below is an example of a basic conversion. Somethings to note is that you are not able to auto bump build numbers when using the app.config.js but there are other ways to accomplish this.&lt;/p&gt;
&lt;h4&gt;app.config.js&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function getBundleId() {
    switch (process.env.APP_VARIANT) {
        case &apos;development&apos;: {
            return &apos;com.isom.grant.dev&apos;;
        }
        case &apos;qa&apos;: {
            return &apos;com.isom.grant.qa&apos;;
        }
        case &apos;production&apos;: {
            return &apos;com.isom.grant.prod&apos;;
        }
        default: {
            return &apos;com.isom.grant.dev&apos;;
        }
    }
}

export default {
    name: getName(),
    ...
    ios: {
        bundleIdentifier: getBundleId(),
        ...
    },
    android: {
        package: getBundleId(),
        ...
    },
    extra: {
        APP_VARIANT: process.env.APP_VARIANT || &apos;development&apos;,
    },
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;eas.json&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &quot;build&quot;: {
        &quot;development&quot;: {
            &quot;distribution&quot;: &quot;internal&quot;,
            &quot;developmentClient&quot;: true,
            &quot;autoIncrement&quot;: false,
            &quot;env&quot;: {
                &quot;APP_VARIANT&quot;: &quot;development&quot;
            },
            &quot;android&quot;: {
                &quot;buildType&quot;: &quot;apk&quot;
            }
        },
        ...
    },
    ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now simply providing the &lt;code&gt;--profile&lt;/code&gt; when running an &lt;code&gt;eas build&lt;/code&gt; command will set the bundle identifier and package name of your app. You can do this for environment variables as well.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Constants.expoConfig?.extra?.APP_VARIANT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above snippet will return the value set during build at runtime.&lt;/p&gt;
&lt;h2&gt;Passing Environment Variables to EAS Update&lt;/h2&gt;
&lt;p&gt;Environment variables are fairly straightforward for updates. You will need to configure use interactive mode to attach branches to variants. But after that, you will just include the environment variable(s) before the command like any other CLI.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;APP_VARIANT=&apos;production&apos; eas update -p all --branch prod
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To wrap things up, if you&apos;re using Expo to build your mobile app, setting up multiple environments or variants can be a big help. By converting your app.json file to a app.config.js file, you&apos;ll have more control over your app&apos;s settings and can utilize environment variables for native pieces. And if you need to pass on environment variables to the EAS update, it&apos;s pretty simple. Just include the variables before you run the update command and you&apos;re good to go. All in all, Expo makes it easy for you to build, test, and publish your app to both the App Store and Google Play with a smooth and stress-free experience.&lt;/p&gt;
</content:encoded></item><item><title>Notion 101 for Software Engineers</title><link>https://grantisom.com/2023/01/14/notion-for-software.html</link><guid isPermaLink="true">https://grantisom.com/2023/01/14/notion-for-software.html</guid><description>As a software engineer, staying organized and on top of your projects can be a challenge. Not only...</description><pubDate>Sat, 14 Jan 2023 20:28:58 GMT</pubDate><content:encoded>&lt;p&gt;As a software engineer, staying organized and on top of your projects can be a challenge. Not only are there many projects going on at times, but how your work is managed can be split between tools. We use Jira, Confluence, and Github at work, I use Todoist to deal with personal tasks and projects, and then Notion sits in between all of those helping to bring the information needed to the front. In this blog post, I will walk through how I use Notion as a software engineer to keep my work running smoothly and streamline how I capture interesting or important information.&lt;/p&gt;
&lt;h2&gt;The Work Log&lt;/h2&gt;
&lt;p&gt;Keeping a Daily/Weekly Work Log is a great way to document what you worked on during the day and any questions you want to bring up at Standup the next morning. This can be done in any note-taking tool, but I find that Notion is particularly useful for this purpose. I have a daily template set up that I use to log my work and any questions I may have. This template includes sections for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tasks completed&lt;/li&gt;
&lt;li&gt;Problems encountered&lt;/li&gt;
&lt;li&gt;Questions for the team&lt;/li&gt;
&lt;li&gt;Things to follow up on&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This has been incredibly helpful in keeping my update concise and to the point while not forgetting something I need help with. The work log has also served as a great utility when trying to track down something I worked on that is not easily found in Jira or Github. I highly recommend every engineer at least try keeping a log for a couple months and see if they get a benefit from it.&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/f159196842.png&quot; alt=&quot;Notion Work Log calendar showing weekly entries in January 2023&quot;&gt;
&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/fa6c5dfe53.png&quot; alt=&quot;Notion weekly work log page with dated daily engineering notes&quot;&gt;
&lt;/p&gt;
&lt;h2&gt;Templates&lt;/h2&gt;
&lt;p&gt;One of the most powerful features of Notion is its ability to create custom templates. These templates can be used for everything from meeting notes to planning sprints, and they can be easily shared with other members of your team. These allow you to spend less time setting things up and more time capturing the important information needed during meetings. For example, I have a template set up for meeting notes that includes sections for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Agenda&lt;/li&gt;
&lt;li&gt;Attendees&lt;/li&gt;
&lt;li&gt;Discussions&lt;/li&gt;
&lt;li&gt;Action items&lt;/li&gt;
&lt;li&gt;Follow-up&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This has been extremely helpful in keeping track of what was discussed during meetings and what the next steps are. I also have a template for capturing my achievements and work during the year. This way when it comes time for reviews I have a breakdown of all of the things I was proud of throughout the year. This can be very helpful at companies of all sizes and makes your manager’s life easier when understanding your impact. There are endless ways templates can be used, but I think this is a major reason to use Notion or a similar tool over something like Apple Notes.&lt;/p&gt;
&lt;h2&gt;Web Clipper&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.notion.so/web-clipper&quot;&gt;The Notion Web Clipper&lt;/a&gt; has fully replaced Instapaper or Chrome/Safari Reading Lists for me. It allows me to capture helpful articles, links, even things like README’s from Github. It works on pretty much any browser and mobile and is an easy way to quickly capture web pages that you want to reference later. It also tries to pull the text into a page so you can search over the content of all of your saved links. The Web Clipper is an extremely helpful tool when you are starting a new project as well since you can gather a massive amount of information and references very quickly without having to keep a million tabs open.&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/5fd90bfbf1.png&quot; alt=&quot;Notion illustration of a person clipping a page with scissors&quot;&gt;
&lt;/p&gt;
&lt;h2&gt;Teamspaces and Notion AI&lt;/h2&gt;
&lt;p&gt;Notion&apos;s recent release of team spaces and Notion AI has made it an even more versatile tool for software engineers. Team spaces allows for easy collaboration and sharing of templates, databases and pages within a team, making it a great tool for managing projects and tasks together. Notion AI, on the other hand, offers a range of features to help you with your workflow such as automated reminders, scheduling, and natural language processing to quickly find information. These features have the potential to greatly improve productivity and collaboration within teams. Additionally, with a growing community and tons of resources online to maximize Notion’s potential, it is an extremely versatile tool that can help software engineers stay organized and get work done faster.&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/6647450a28.png&quot; alt=&quot;Notion AI introduction graphic showing writing-assistant actions&quot;&gt;
&lt;/p&gt;
</content:encoded></item><item><title>9 Must-Read Books for Software Engineers in 2023</title><link>https://grantisom.com/2023/01/02/mustread-books-for.html</link><guid isPermaLink="true">https://grantisom.com/2023/01/02/mustread-books-for.html</guid><description>As a software engineer, staying up-to-date with the latest developments and best practices...</description><pubDate>Mon, 02 Jan 2023 23:43:56 GMT</pubDate><content:encoded>&lt;p&gt;As a software engineer, staying up-to-date with the latest developments and best practices is essential for growth. One of my favorite (and what I feel is overlooked) methods for growth is reading books. We spend a large part of our day reading Stack Overflow and blog posts, but books have really helped me see things in a different light or understand something I do not come across on a daily basis.&lt;/p&gt;
&lt;p&gt;In 2022, I read a number of books that were extremely valuable to my career as a software engineer. Here are nine that I highly recommend for software engineers at all stages of their careers to consider adding to their reading lists for 2023:&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/ec18f32904.jpg&quot; alt=&quot;Cover of Build by Tony Fadell&quot;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/59783101-build&quot;&gt;&quot;Build: An Unorthodox Guide to Making Things Worth Making&quot;&lt;/a&gt; by Tony Fadell is a must-read for anyone looking to create things that matter. Fadell shares his unique perspective on how to focus on small, achievable goals and pivot when necessary.&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/475c3984d0.jpg&quot; alt=&quot;Cover of The Phoenix Project&quot;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/38191426-the-phoenix-project&quot;&gt;&quot;The Phoenix Project: A Novel about IT, DevOps, and Helping Your Business Win&quot;&lt;/a&gt; by Gene Kim, Kevin Behr, and George Spafford is a captivating story about an IT manager who must turn around a failing project. Along the way, he learns about the principles of DevOps and how to apply them to his work.&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/7337cde14c.jpg&quot; alt=&quot;Cover of The Hard Thing About Hard Things&quot;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/20657434-the-hard-thing-about-hard-things&quot;&gt;&quot;The Hard Thing About Hard Things: Building a Business When There Are No Easy Answers&quot;&lt;/a&gt; by Ben Horowitz offers candid and practical advice on the challenges of entrepreneurship. Horowitz covers topics such as hiring, firing, and managing through tough times, and provides valuable insights on how to navigate the ups and downs of building a business.&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/a97bedecb3.jpg&quot; alt=&quot;Cover of Structure and Interpretation of Computer Programs&quot;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/43713.Structure_and_Interpretation_of_Computer_Programs&quot;&gt;&quot;Structure and Interpretation of Computer Programs,&quot;&lt;/a&gt; also known as the &quot;Wizard Book,&quot; is a classic text that covers the fundamental concepts of computer science and programming using the Lisp language. This book is a must-read for any serious software engineer.&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/5f538d59de.jpg&quot; alt=&quot;Cover of A Philosophy of Software Design&quot;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/43701534-a-philosophy-of-software-design&quot;&gt;&quot;A Philosophy of Software Design&quot;&lt;/a&gt; by John Ousterhout argues that good software design is all about simplicity and clarity. He offers practical advice on how to achieve these qualities in your own code. This is a good one to read even on a yearly basis.&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/45d7f5784d.jpg&quot; alt=&quot;Cover of The Pragmatic Programmer&quot;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/50701156-the-pragmatic-programmer&quot;&gt;&quot;The Pragmatic Programmer: From Journeyman to Master&quot;&lt;/a&gt; by Andrew Hunt and David Thomas is a comprehensive guide to becoming a better software engineer. It covers a wide range of topics, including debugging, testing, and refactoring, and is filled with useful tips and techniques. Make sure to pick up the 20th anniversary edition.&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/e1d2ad7014.jpg&quot; alt=&quot;Cover of An Elegant Puzzle&quot;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/45303387-an-elegant-puzzle&quot;&gt;&quot;An Elegant Puzzle: Systems of Engineering Management&quot;&lt;/a&gt; by Will Larson discusses the challenges and opportunities of managing software development teams. Larson covers topics such as building effective processes, setting goals, and creating a positive culture.&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/a37debf5ab.jpg&quot; alt=&quot;Cover of Software Engineering at Google&quot;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/48816586-software-engineering-at-google&quot;&gt;&quot;Software Engineering at Google: Lessons Learned from Programming Over Time&quot;&lt;/a&gt; by Titus Winters, Tom Manshreck, and Hyrum Wright offers a behind-the-scenes look at how Google approaches software development. It covers topics such as code review, testing, and technical debt, and provides valuable insights into how to build reliable and scalable systems.&lt;/p&gt;
&lt;p&gt;

&lt;img src=&quot;https://grantisom.com/images/96ce2ec5a6.jpg&quot; alt=&quot;Cover of Inspired by Marty Cagan&quot;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/36645100-inspired&quot;&gt;&quot;INSPIRED: How to Create Tech Products Customers Love&quot;&lt;/a&gt; by Marty Cagan is a must-read for anyone looking to create successful tech products. Cagan covers topics such as defining the product vision, building a strong team, and gathering customer feedback, and provides practical advice on how to apply these principles to your own work. Commonly recommended for product managers, I think its a great read for engineers as well as it will help when working cross-functionally with your product team.&lt;/p&gt;
&lt;p&gt;These books provided invaluable insights and kept me motivated and inspired throughout the year. &quot;An Elegant Puzzle&quot; was particularly helpful in figuring out how to more effectively handle support issues within my team, while &quot;Software Engineering at Google&quot; offered valuable guidance as we were setting up a new project and repository. I hope this list of recommendations will provide some useful reading options for you in 2023 as you continue to grow and develop in your software engineering career.&lt;/p&gt;
</content:encoded></item></channel></rss>