<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>Davis Haupt&apos;s Blog</title><description>Thoughts and writeups from Davis Haupt.</description><link>https://davi.sh/blog/</link><language>en-us</language><item><title>Ideas for an Agent-Oriented Programming Language</title><link>https://davi.sh/blog/2026/02/markov-ideas/</link><guid isPermaLink="true">https://davi.sh/blog/2026/02/markov-ideas/</guid><description>&lt;p&gt;Software development is changing. Tool calling, inference scaling and RL with Verifiable
Rewards have combined over the past year to enable agent harnesses like Claude Code which
can reliably navigate, modify and contribute to large codebases.&lt;/p&gt;
&lt;p&gt;LLMs scale amazingly well with the amount of training data you throw at them. But I’ve
been thinking about how to build tools that work alongside the characteristics of LLMs
rather than language models needing to learn how to work with existing human-centric tools
during training.&lt;/p&gt;
&lt;p&gt;I have a hunch that a programming environment built around the strengths and limitations
of autoregressive LLMs can lead to cheaper and higher-quality agent-powered
development. How could we prove out that hypothesis? One would first need to design a
language that aligns with how LLMs “think”. What would such a language look like? In this
post I put forward some ideas for a language called Markov that I think would fit the
bill.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id=&quot;humans-should-be-able-to-read-and-edit-markov-code&quot;&gt;Humans should be able to read and edit Markov code&lt;/h2&gt;
&lt;p&gt;First and foremost, Markov will still be human-readable and editable. Agents excel at
generating boilerplate, but the core business logic of any system is where the most
attention to detail will always need to be. Most programmers can get by today without ever
digging into TCP packet captures or assembly code. That doesn’t mean there’s &lt;em&gt;never&lt;/em&gt; a
good reason to do so. I wrote a few months ago in &lt;a href=&quot;../../2025/10/thinking-with-llms&quot;&gt;Thinking about Thinking with
LLMs&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[T]he &lt;em&gt;best&lt;/em&gt; programmers aren’t the ones that make the widest use of the highest
abstractions. That’ll continue to be those who dig down and understand what’s happening
at a deeper level — that understanding will always lead to more deft use of any tools
that programmers have at their disposal.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Transparency into the source code is as much ideological as it is practical. Over the next
few years agents will likely start independently building systems end-to-end. In that
world, interpretability of AI-generated systems becomes as important as interpretability
into the models themselves. We may not have transparency today into the reasons an LLM
might make the decisions it does,&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; but we should discourage models from developing
incomprehensible “neuralese” and keep developing tools which allow for human insight to
continue to play a role.&lt;/p&gt;
&lt;h2 id=&quot;sum-types-for-fast-compiler-feedback&quot;&gt;Sum types for fast compiler feedback&lt;/h2&gt;
&lt;p&gt;Agents thrive on feedback. It helps them stay on the assigned task without falling down
unproductive rabbit holes. Unit tests are important here, but static analysis will always
faster and less error-prone than running code. Markov will have strong, static types and
exhaustive pattern matching to encourage &lt;strong&gt;&lt;a href=&quot;https://web.archive.org/web/20260205183806/https://functional-architecture.org/make_illegal_states_unrepresentable/&quot;&gt;making illegal states
unrepresentable&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Rust’s reliance on sum types and compiler-enforced exhaustiveness checks in pattern
matching enables fearless refactoring and extension of existing code. A human can spend a
few minutes adding a variant to an existing type after thinking hard (or planning with an
LLM) about the best way to represent some new functionality. The agent can then propagate
that change outward through the codebase, completing now-inexhaustive pattern matches and
following boilerplate patterns it encounters.&lt;/p&gt;
&lt;p&gt;Markov should lean into this pattern: a human (or very smart LLM) makes the decisions
about core data structures and interfaces, and an agent will propagate that change
throughout the rest of the codebase. Today this second step needs to be carried out by a
frontier model but in the future a smaller/cheaper/faster model can probably do this
job sufficiently well.&lt;/p&gt;
&lt;p&gt;This is a pattern that has been extremely effective in my own work with coding agents
since Claude 4.5 was released: focus on core types and abstractions and let the agent
plumb it through. With a coding agent I don’t have to be as careful about future-proofing
my types or using fancy language features to avoid boilerplate when adding new
functionality. Strong interface boundaries and full sum types help keep an LLM on guard
rails.&lt;/p&gt;
&lt;p&gt;Edoardo Vacchi wrote a post late last year called &lt;a href=&quot;https://blog.evacchi.dev/posts/2025/11/09/the-return-of-language-oriented-programming/&quot;&gt;The Return of Language Oriented
Programming&lt;/a&gt;
about LLM-enabled DSL toolchains. He describes how an LLM can generate a spec and an
interpreter for a DSL and then generate business logic in that DSL. This is reminiscent of
&lt;a href=&quot;https://www.oilshell.org/blog/2022/02/diagrams.html&quot;&gt;narrow waist software engineering&lt;/a&gt;
that I talked about &lt;a href=&quot;/weekly/2025-11-09/&quot;&gt;in my newsletter&lt;/a&gt; in reference to &lt;a href=&quot;https://mitchellh.com/writing/non-trivial-vibing&quot;&gt;Mitchell
Hashimoto’s article about non-trivial vibe
coding&lt;/a&gt;. A well-defined protocol between
different parts of the system — in Mitchell’s post, a view model; in Edoardo’s, a DSL —
reduces the “cognitive” load and can make either a human programmer or an LLM much more
effective.&lt;/p&gt;
&lt;p&gt;I prefer embedded syntax within a strongly typed language&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; to a DSL backed by a full
compiler toolchain, but I agree with the main thrust of the article. This pattern is
probably most commonly associated with more academic languages like OCaml and
Haskell. However, embedded DSLs form the foundation of modern machine learning with
Python-based DSLs like PyTorch and
&lt;a href=&quot;https://triton-lang.org/main/index.html&quot;&gt;Triton&lt;/a&gt;. Edoardo addresses this towards the end
but asserts that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The cost of defining your own syntax instead of leaning onto the host language’s is now
much lower; in fact, I would even dare to say that you might want to prefer flexible
syntax, so that you will be able to optimize for token cost.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think Edoardo presents a bit of a straw man argument. Writing a lexer, parser and
interpreter might be cheaper with LLMs, but &lt;em&gt;maintaining&lt;/em&gt; that stack is still a nonzero
amount of effort that relying on a host language with good DSL support can obviate.&lt;/p&gt;
&lt;h2 id=&quot;compiler-errors-as-prompts&quot;&gt;Compiler errors as prompts&lt;/h2&gt;
&lt;p&gt;Compiler errors are human-readable. There’s short explanations with error codes and links
to external documentation to stay within a small terminal window, and often some ASCII art
drawing an arrow to the specific problematic row and column within a file. LLMs can only
interpret these errors since Stack Overflow and other sites are present in the training set
and the aggressive post-training with verifiable rewards that conditions them to
understand these errors.&lt;/p&gt;
&lt;p&gt;But what would compiler errors for LLMs look like? They would be phrased as prompts with
suggestions on how to fix an error formatted as diffs rather than ASCII diagrams. When
possible the compiler should explain the cases that a specific error may happen and give
the agent possible directions of investigation.&lt;/p&gt;
&lt;h2 id=&quot;token-optimized-syntax&quot;&gt;Token optimized syntax&lt;/h2&gt;
&lt;p&gt;Markov’s syntax should be optimized to make the most effective use of the context
window. There’s already some promising results for &lt;a href=&quot;https://toonformat.dev/&quot;&gt;TOON&lt;/a&gt;, an
alternative rendering of JSON whose grammar uses elements that LLMs need to represent
across multiple tokens. Token-optimized code is cheaper for agents to process&lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; and also
potentially easier for them to understand: &lt;a href=&quot;https://toonformat.dev/guide/benchmarks.html&quot;&gt;TOON shows slightly better retrieval accuracy
compared to JSON&lt;/a&gt; even though the models
have been trained on heaps and heaps of JSON.&lt;/p&gt;
&lt;p&gt;“The Return of Language Oriented Programming” touches on token-optimization, too. LLMs
trained on a general corpus of English use fewer tokens to represent common words than
they do to represent shorter abbreviations that human authors often use for the sake of
write-ability, like Rust’s &lt;code&gt;fn&lt;/code&gt; that marks functions definitions. I firmly believe that
code should be optimized for readability over write-ability, and it’s pretty cool that if
Markov was designed to be simple for LLMs to write efficiently would also probably be more
readable for humans than one designed for humans to write efficiently. This nicely feeds
back into our first goal.&lt;/p&gt;
&lt;p&gt;LLMs are horrendously bad at counting,&lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; though, and so Ruby’s &lt;code&gt;begin&lt;/code&gt;/&lt;code&gt;end&lt;/code&gt; lexical
scopes will probably be preferable to Python’s indentation-based scoping.&lt;/p&gt;
&lt;p&gt;Martin Alderson &lt;a href=&quot;https://martinalderson.com/posts/which-programming-languages-are-most-token-efficient/&quot;&gt;surveyed existing languages and compared their token
efficiency&lt;/a&gt;. Python
was close to the top, but Clojure came in second place and Haskell and OCaml both beat out
TypeScript. So there’s some evidence that functional programming idioms can be beneficial
on this axis, too.&lt;/p&gt;
&lt;h2 id=&quot;interoperability-is-a-non-goal&quot;&gt;Interoperability is a non-goal&lt;/h2&gt;
&lt;p&gt;Most popular new languages of the past decade have all piggy-backed on existing software
ecosystems: Swift on Objective-C; Kotlin on Java; TypeScript on JavaScript.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-6&quot; id=&quot;user-content-fnref-6&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; Allowing
programmers to gradually migrate over their existing codebases and rely on existing
trusted dependencies has been the characteristic behind each of those languages’ rises.&lt;/p&gt;
&lt;p&gt;This benefit degrades with coding agents. Today’s agents have been shown to &lt;a href=&quot;https://simonwillison.net/2025/Dec/15/porting-justhtml/&quot;&gt;reliably port
existing libraries to new
languages&lt;/a&gt; with a fraction of the
effort. Drew Breunig even experimented with &lt;a href=&quot;https://www.dbreunig.com/2026/01/08/a-software-library-with-no-code.html&quot;&gt;a software library that has no code checked
in to source control at
all&lt;/a&gt;. When you
can generate new functionality in less time than it takes to research the right library,
the ability for Markov to call into an existing ecosystem without an FFI is less critical.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Well, you may be wondering now, where are the code snippets? Unfortunately not in this
post. I’ve had these ideas floating around in my head for months now, and wanted to get it
down on paper and out into the world.&lt;/p&gt;
&lt;p&gt;Armin Ronacher recently wrote up &lt;a href=&quot;https://lucumr.pocoo.org/2026/2/9/a-language-for-agents/&quot;&gt;A Language For
Agents&lt;/a&gt; which touches on similar
themes. Armin’s post is worth a read, but Armin focuses on different aspects of a language
and its ecosystem than I’ve done in this post.&lt;/p&gt;
&lt;p&gt;We’ll see where all these ideas go! Luckily, coding agents make starting a new language
easier than ever.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;This fact keeps me up at night. My writing in this article might seem more
accelerationist than my usual tone. I’m not &lt;em&gt;sure&lt;/em&gt; that this future is coming, but I
think it’s important to be prepared for and for these tools to be crafted
intentionally rather than stumbled in to. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;Enforced with sum types or their more expressive cousins,
&lt;a href=&quot;https://dev.realworldocaml.org/gadts.html&quot;&gt;GADTs&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;The underlying LLM calls are often billed per-token. &lt;a href=&quot;#user-content-fnref-4&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;How many “R”s are in strawberry, again? &lt;a href=&quot;#user-content-fnref-5&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-6&quot;&gt;
&lt;p&gt;Rust is the exception here (and is slightly older than the others), but it really
filled an empty niche rather than improving on the ergonomics of a predecessor. &lt;a href=&quot;#user-content-fnref-6&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><pubDate>Sun, 15 Feb 2026 17:00:00 GMT</pubDate></item><item><title>Reviewing Project Hail Mary by Andy Weir</title><link>https://davi.sh/reading/2026/project-hail-mary/</link><guid isPermaLink="true">https://davi.sh/reading/2026/project-hail-mary/</guid><description>&lt;p&gt;I read &lt;em&gt;Project Hail Mary&lt;/em&gt; for the first time soon after it came out and devoured it in a
single sitting — ruining my night’s sleep in the process. Even though I already knew the
twists and turns of the plot, I still couldn’t put it down on this second
read-through.&lt;/p&gt;
&lt;p&gt;Just like &lt;em&gt;The Martian&lt;/em&gt;, the scientific method infuses everything in this book. Moreso
than Weir’s first novel, &lt;em&gt;Project Hail Mary&lt;/em&gt; has a much more compelling “human element”
about the ways we can work together across difference.&lt;/p&gt;
&lt;p&gt;The narration and dialog both have a great and witty rhythm to them that keep the story
fun to read throughout. I’m excited to see how it translates to the screen next month!&lt;/p&gt;</description><pubDate>Tue, 10 Feb 2026 17:00:00 GMT</pubDate></item><item><title>Reviewing Culpability by Bruce Holsinger</title><link>https://davi.sh/reading/2026/culpability/</link><guid isPermaLink="true">https://davi.sh/reading/2026/culpability/</guid><description>&lt;p&gt;Who’s at fault in a fatal accident involving a self-driving car? &lt;em&gt;Culpability&lt;/em&gt;
investigates the abstract morality of the trolley problem while exploring the emotional
fallout among the family who were the lucky survivors of the crash.&lt;/p&gt;
&lt;p&gt;The book is firmly rooted in the perspective of the narrator Noah, a successful but
run-of-the-mill lawyer at a Maryland law firm, his brilliant wife Lorelei, a digital
ethicist at the top of her field, and their three teenage children. It’s an engaging book
about parenthood and coming of age that has almost as much to say about 21st-century
privilege and the American Dream as it does about ethical algorithms and artificial
intelligence.&lt;/p&gt;
&lt;p&gt;I don’t think I’ve read anything that spends as much time on the everyday considerations
and dynamics of marriage or fatherhood. The book was 350 Kindle pages and I was able to
finish it in a few sessions over the course of two days.&lt;/p&gt;
&lt;p&gt;In my own media bubble I hear a lot about three-letter acronyms like Artificial
Superintelligence (ASI) and Universal Basic Income (UBI); forces that some argue will move
nations in the coming decades. Holsinger reminds us that we ought to give the same
attention — if not more — to how the systems we’re integrating into our lives &lt;em&gt;today&lt;/em&gt;
will affect us and our relationships with the people we care about most.&lt;/p&gt;
&lt;p&gt;Digital systems by definition are predicated on ones and zeros: absolute truth and absolute
falsehood. Even still, the emergent behavior of modern distributed systems on top of these
simple rules can be so uninterpretable to most human understanding that the best our
existing moral frameworks and legal systems can do is just throw up their metaphorical
hands and shrug their anthropormorphized shoulders.&lt;/p&gt;
&lt;p&gt;During the COVID vaccine rollout, a hospital system &lt;a href=&quot;https://web.archive.org/web/20260101232819/https://www.technologyreview.com/2020/12/21/1015303/stanford-vaccine-algorithm/&quot;&gt;put blame on an
algorithm&lt;/a&gt;
for a blatantly unfair apportionment of vaccine resources that benefited tenured medical
faculty over resident physicians on the front lines. LLMs and diffusion models are trained
to mimic authors and artists. This can be argued to fit in the bucket of copyright fair
use in our legal system while still &lt;em&gt;feeling&lt;/em&gt; wrong to many of us. Our system is built
upon the assumption that mortal, moral humans are the progenitors and beneficiaries of
intellectual property. That hasn’t been true for a while (the Walt Disney Company
&lt;a href=&quot;https://web.archive.org/web/20260212104405/https://web.law.duke.edu/cspd/mickey/&quot;&gt;famously put their finger on the
scale&lt;/a&gt;),
however Generative AI models that take millions of dollars to train but then can be scaled
out and deployed widely make the limitations of that assumption even more apparent.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Culpability&lt;/em&gt; also touches on the limits of our legal system, especially how it intersects
with power and privelege. Noah is a lawyer and the story’s backdrop is the the police
investigation of the car crash. Law itself is a field that has attempted to coerce human
experience into discrete judgment for virtually &lt;a href=&quot;https://en.wikipedia.org/wiki/Code_of_Hammurabi&quot;&gt;all of recorded
history&lt;/a&gt;. Machine code has voltage
differentials giving way to ones and zeroes; neural networks have probabilistic
predictions &lt;a href=&quot;https://en.wikipedia.org/wiki/Sigmoid_function&quot;&gt;flattened&lt;/a&gt; into deterministic
output. The legal system has judges and juries which dispense innocent and guilty
verdicts.&lt;/p&gt;
&lt;p&gt;I’m reminded of &lt;a href=&quot;https://web.archive.org/web/20250818154250/https://whitney.org/exhibitions/programmed/art?section=2&amp;#x26;subsection=3&quot;&gt;&lt;em&gt;Broken
Volume&lt;/em&gt;&lt;/a&gt;,
sculpture by Cheyney Thompson I saw at The Whitney in 2019. 3D random walks were generated
by CAD software and cast into concrete. One of the pieces broke in half upon installation,
where it stood for the length of the exhibition. Random walks have the simplest of rules
and can create beautiful shapes, but they don’t know about gravity. Algorithms follow
defined rules that are encoded within them. All digital systems are based on our own
incomplete and imperfect assumptions about how the things we create will behave when they
encounter the world that we inhabit. Even when deep learning systems start to pick out
their own patterns in their training data, they’re learning from the biases and
imperfections of what we choose to include and what we choose to omit from their training.&lt;/p&gt;
&lt;p&gt;People die in a car crash where a self-driving car is involved. Who’s at fault? We get
closer and closer to an answer throughout the story, but never quite reach it. Can there
be a singular answer? Do we need there to be one?&lt;/p&gt;</description><pubDate>Tue, 03 Feb 2026 17:00:00 GMT</pubDate></item><item><title>Reviewing Mistborn: The Final Empire by Brandon Sanderson</title><link>https://davi.sh/reading/2026/mistborn/</link><guid isPermaLink="true">https://davi.sh/reading/2026/mistborn/</guid><description>&lt;p&gt;After &lt;a href=&quot;../2025/tress&quot;&gt;last year&lt;/a&gt; I wanted to give Sanderson another
shot. This time I chose one of his most popular series rather than a newer, spin-off work.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Mistborn&lt;/em&gt; stuck pretty closely to some common fantasy tropes. I thought Sanderson’s main
characters had more depth to them in the book, which contributed to me enjoying the book
slightly more than &lt;em&gt;Tress&lt;/em&gt;. Allomancy is an interesting and certainly well-thought out
magic system, but ultimately the softer, more mystical magic of &lt;em&gt;Lord of the Rings&lt;/em&gt; or &lt;em&gt;A
Song of Ice and Fire&lt;/em&gt; is more to my taste these days.&lt;/p&gt;
&lt;p&gt;I struggled the most with the dialog in this book. Sanderson’s word choices for his
characters often felt grating to me and disarmingly modern for an early industrial fantasy
setting. The way that members of the city’s criminal underground talked about their
profession felt awkward in the way they leaned in to modern tropes the reader would
understand rather than in-universe exposition. I don’t think any character exactly said
that they were “doing the heist” or something &lt;em&gt;that&lt;/em&gt; cheesy, but many exchanges weren’t
far off.&lt;/p&gt;
&lt;p&gt;I didn’t really put my finger on this until starting to read &lt;em&gt;The Well of Ascension&lt;/em&gt;, the
sequel which I picked up but have stopped reading for now. But here are a few quotes that
hopefully capture what I mean:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So I began spreading rumors in the Western Dominance, then made myself one of Lord
Cett’s advisors&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The way this glosses over an indeterminate amount of time, and how a character casually
talks about penetrating the inner circle of a rival, did not feel like how someone would
talk about an operation of this importance or risk.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Maybe this won’t work — maybe after a couple months of being besieged, we’ll just end
up giving away the city anyway.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;“Being besieged” just really grated on me here. I could think of twenty ways to phrase this
that sound more natural, starting with “maybe after a couple months under siege”.&lt;/p&gt;
&lt;p&gt;These examples might seem like nitpicking, but I picked up at least one of these every
chapter or two during &lt;em&gt;Mistborn&lt;/em&gt;, and it really did affect my immersion in the world.&lt;/p&gt;</description><pubDate>Sat, 31 Jan 2026 17:00:00 GMT</pubDate></item><item><title>Reviewing A Tree Grows in Brooklyn by Betty Smith</title><link>https://davi.sh/reading/2025/a-tree-grows-in-brooklyn/</link><guid isPermaLink="true">https://davi.sh/reading/2025/a-tree-grows-in-brooklyn/</guid><description>&lt;p&gt;&lt;em&gt;A Tree Grows in Brooklyn&lt;/em&gt; is a quintessential coming of age story. Not just for Francie — the protagonist who we follow from the age of 10 through her going off to college — but also for New York itself, which we see change through industrialization along with the characters.&lt;/p&gt;
&lt;p&gt;The first part (of five) is on the slower side, with barely-connected vignettes that paint a picture of what it was like to grow up in an immigrant community in Williamsburg around the turn of the century. These ensure that you really feel connected to Francie’s family and neighborhood by the time the proper story gets going in the second part.&lt;/p&gt;
&lt;p&gt;I wonder why this wasn’t a part of my high school English curriculum: the book has a lot to say about everything from childhood and motherhood to the American Dream. I’m happy I picked it up, I enjoyed this more than I expected to!&lt;/p&gt;</description><pubDate>Sat, 20 Dec 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing The Strength of the Few by James Islington</title><link>https://davi.sh/reading/2025/strength-of-the-few/</link><guid isPermaLink="true">https://davi.sh/reading/2025/strength-of-the-few/</guid><description>&lt;p&gt;This is a tough sequel to review without any spoilers for &lt;em&gt;The Will of the Many&lt;/em&gt;, so I’m
not going to attempt it. &lt;strong&gt;Spoiler warning for Book I below!&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The Strength of the Few&lt;/em&gt; picks up right where &lt;a href=&quot;/reading/2025/will-of-the-many&quot;&gt;&lt;em&gt;The Will of the
Many&lt;/em&gt;&lt;/a&gt; left off: Vis, the protagonist, has been
simultaneously copied into two other worlds called Luceum and Obiteum while
his story also continues back in the world of Book I, called Res. Somewhat necessarily,
each storyline is not as deep as the story in the first book.&lt;/p&gt;
&lt;p&gt;The politics and motivations that made me excited about &lt;em&gt;The Will of the Many&lt;/em&gt; fade into
the background of this installment. The Catenan Senate in Res is relegated to a third of
the chapters, and Vis’s unfamiliarity with the other two worlds make him much less of an
astute observer of the political machinations around him there.&lt;/p&gt;
&lt;p&gt;I mentioned in my last review that the world of Res felt shallow for how physically large
it was. I can better forgive some of that that shallowness here considering how different
Obiteum and Luceum feel. &lt;em&gt;The Strength of the Few&lt;/em&gt; shows that Islington can build deep
history and culture into his worlds when he wants to, but he sometimes thinks its better
to keep readers at an information disadvantage for the sake of the plot.&lt;/p&gt;
&lt;h2 id=&quot;luceum&quot;&gt;Luceum&lt;/h2&gt;
&lt;p&gt;I absolutely loved the Luceum storyline. Readers are introduced to an entirely new world
with just enough detail to keep us engaged and excited to learn more without overwhelming
us with exposition. The way Islington uses Gaelic to represent Luceum’s primary language
is great. From most readers’ perspective it’s the same as if he’s made up his own language
without needing to be a &lt;a href=&quot;https://en.wikipedia.org/wiki/Philology_and_Middle-earth&quot;&gt;philologist on top of being a fantasy
author&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The worldbuilding in Luceum felt much more like what &lt;a href=&quot;/reading/author/ursula-k-le-guin&quot;&gt;Ursula K. Le
Guin&lt;/a&gt; utilizes in her &lt;em&gt;Earthsea&lt;/em&gt; books rather than the
exposition-heavy information dumps that Islington uses elsewhere that reminds me more of
&lt;em&gt;The Broken Earth Trilogy&lt;/em&gt;’s latter entries.&lt;/p&gt;
&lt;p&gt;This version of Vis also has some real character growth here in a way that was completely
missing in Obiteum and unsatisfying in Res.&lt;/p&gt;
&lt;h2 id=&quot;obiteum&quot;&gt;Obiteum&lt;/h2&gt;
&lt;p&gt;Obiteum reminded me of Cittagazze from &lt;em&gt;The Subtle Knife&lt;/em&gt;: the world is clearly broken,
and something related to the existence of multiple worlds was what broke it. In general I
saw more influences from &lt;em&gt;His Dark Materials&lt;/em&gt; than &lt;em&gt;Red Rising&lt;/em&gt; throughout &lt;em&gt;The Strength
of the Few&lt;/em&gt;. The initial imagery of Gleaners parallel Spectres in a very fun way.&lt;/p&gt;
&lt;p&gt;Once we move from the rebel hideout of Qabr into the city of Duat, though, things start to
fall through the cracks. My book club all agreed that we needed a map of Duat to go along
with Vis’s descriptions of how he is moving through the city. I would love to have learned
more about the society in Duat, its religion, and the totality of how Will works in
Obiteum. So much time is spent on the undead Iunctii, but Vis’s entrance to Obiteum
introduces us to a variety of Will-based devices that aren’t mentioned at all in parts II
and III.&lt;/p&gt;
&lt;h2 id=&quot;res&quot;&gt;Res&lt;/h2&gt;
&lt;p&gt;I enjoyed learning more about the mechanics of Will and how Catenan society operates
outside the confines of the Academy. Vis is fully on the inside of “the system” now, and
seeing how Will affects everything from politics to athletics to combat was great.&lt;/p&gt;
&lt;p&gt;The Catenan civil war completely changes the setting and unfortunately as much as we spent
learning about Catenan politics in Book I and the first half of Book II, there’s so many
more forgettable Catenan senators introduced whose motivations are explained mostly
through exposition dumps.&lt;/p&gt;
&lt;p&gt;All in, Part III’s Res storyline felt very rushed. Vis makes some consequential decisions which
feel like they come out of nowhere. A new plan from Vis and Eidhin in the final 100 pages
which I assumed would form the Res plotline in Book III is somehow wrapped up in less than
a chapter. While there are strong emotional points, I ultimately don’t truly &lt;em&gt;understand&lt;/em&gt;
why Vis makes the decisions and alliances he does.&lt;/p&gt;
&lt;h2 id=&quot;splitting-the-book&quot;&gt;Splitting the Book&lt;/h2&gt;
&lt;p&gt;There were two very disorienting time jumps in the book: 6 weeks in Obiteum between parts
I and II, and a month in Res between parts II and III. The narration refers to events and
people that happened in the meantime without any indication of missing chapters in between
— both times I had to double-check that my Kindle didn’t glitch out and miss a few dozen
pages. Ultimately I think the location of these time jumps mark out a good place where the
book itself could have been split in two:&lt;/p&gt;
&lt;p&gt;Book II would follow the entire Luceum storyline, Vis’s time in Qabr in Obiteum, and the
Res storyline up until Excesius is killed. This can dive more deeply into how exactly Will
is used in Caten and flesh out the politics of the Senate even more.&lt;/p&gt;
&lt;p&gt;Book III would cover the Catenan civil war and Vis’s infiltration of the city of Duat in
Obiteum.&lt;/p&gt;
&lt;p&gt;The downside of this approach would be that the three storylines would not proceed in
lockstep in time. Maybe Islington is planning some bigger payoff in the next book, but
there was one moment of “synchronism” that did not feel narratively or emotionally
powerful, so I don’t think the parallel narrative gimmick is particularly worth it.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Overall I enjoyed learning more about the world(s) that Islington is building. My
excitement about Luceum mostly counterbalances my confusion in Obiteum and my frustration
with how Res feels even smaller and more condensed than in the first book.&lt;/p&gt;
&lt;p&gt;Backstory isn’t bad in and of itself, but learning more history shouldn’t be &lt;em&gt;the&lt;/em&gt; reason
we keep reading. I don’t like when characters (and readers) learning new information
becomes the main plot tension in a story. When revealed, the “right” answers are often not
as satisfying as the possibilities and theories that readers come up with along the way.
Right now I don’t know what’s a plot hole and what’s a conscious omission that will be
filled in later to support a twist or some other dramatic effect. The only way to find out
is to wait for the rest of the series to be published.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The Expanse&lt;/em&gt; balanced this well: its final installment gave some satisfying answers about
the Protomolecule while leaving enough to interpretation and keeping the conclusion of the
book mostly about the characters we’d gotten to know.&lt;/p&gt;
&lt;p&gt;Considering how the focus of &lt;em&gt;The Strength of the Few&lt;/em&gt; shifted from the motivations of
individual people and groups in Res to the cosmic forces connecting and separating the
three worlds we’re now following, I’m not as optimistic that Islington will stick the
landing.&lt;/p&gt;</description><pubDate>Wed, 19 Nov 2025 17:00:00 GMT</pubDate></item><item><title>Thinking About Thinking With LLMs</title><link>https://davi.sh/blog/2025/10/thinking-with-llms/</link><guid isPermaLink="true">https://davi.sh/blog/2025/10/thinking-with-llms/</guid><description>&lt;p&gt;After reading &lt;a href=&quot;https://www.recurse.com/blog/191-developing-our-position-on-ai&quot;&gt;Developing our position on
AI&lt;/a&gt; from the &lt;a href=&quot;https://www.recurse.com/&quot;&gt;Recurse
Center&lt;/a&gt;, the corresponding entry in &lt;a href=&quot;/weekly&quot;&gt;my newsletter&lt;/a&gt;
grew long enough I’ve decided to break it out into a full blog post. Above and beyond its
specific findings, I think the post charts a path for a more civil and considered mode of
discussion that we should all strive for on the Internet and in our own lives.&lt;/p&gt;
&lt;p&gt;I’ve long been fascinated by the Recurse Center. Throughout my career I’ve been
consistently impressed with alums I’ve crossed paths with, so I’m not particularly
surprised that probably the most balanced and thoughtful take on AI and software
engineering that I’ve read so far has come out of Recurse.&lt;/p&gt;
&lt;h2 id=&quot;discourse-on-the-internet&quot;&gt;Discourse on the Internet&lt;/h2&gt;
&lt;p&gt;I think the article presents a great way to deal with contentious issues within
organizations and how to write about those issues publicly. First, listen to
everyone. Then, acknowledge disagreements and emphasize nuance to make an effort to find
common ground.&lt;/p&gt;
&lt;p&gt;The goal doesn’t need to be having The Correct Take at every moment. People have different
life experiences which often leads them to different top-line beliefs. But beyond those
headlines, where reasonable people state anything from “we’ll all be living in ASI-powered
utopia by 2028” to “LLMs are slurping up all our fresh water”, there is a surprising
amount of common ground.&lt;/p&gt;
&lt;p&gt;In the case of this article, most people agreed that &lt;em&gt;using LLMs to learn new things&lt;/em&gt;
needs to be done with some caution.&lt;/p&gt;
&lt;h2 id=&quot;step-functions-in-abstractions&quot;&gt;Step-Functions in Abstractions&lt;/h2&gt;
&lt;p&gt;This paragraph from the post really resonated with me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[T]utorials and teachers can only get you so far. Ultimately, you must build your own
mental structures. Stack Overflow can be a helpful resource, but blindly following or
copy-pasting from it does little to help you learn. While you can get a lot farther
mindlessly using LLMs than Stack Overflow, the same principle holds true.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The comparison to StackOverflow is one that I’ve seen pop up again and again in
discussions around LLMs. While LLMs can feel revolutionary, it’s not the first time we’ve
seen a step change in software development resources.&lt;/p&gt;
&lt;p&gt;Google made the skill of searching and skimming manuals for reference materials almost
obsolete. O’Reilly still exists, but reference books are a much smaller part of an
engineer’s education than shorter-form blog posts focused on specific tasks&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;StackOverflow made it possible to accomplish many common coding tasks without much upfront
thinking. Most consider copy/pasting StackOverflow answers to be an unfortunate
shortcut. StackOverflow filled a niche, but it didn’t obviate the need for a deep
understanding.&lt;/p&gt;
&lt;p&gt;Software engineering has always had a component focused on automating “white collar” human
work like airline logistics, processing payroll, or organizing and maintaining
libraries&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Programming as a profession has never been immune to this — assemblers
automated away calculating memory offsets for jumping to procedures; garbage collectors
automated memory management; Google Search changed what it meant to RTFM.&lt;/p&gt;
&lt;p&gt;New tools always make it easier to code with a shallower understanding of the
system. Claude Code, Cursor and their ilk are bringing programming closer to natural
language just like C and Java and Python once did.&lt;/p&gt;
&lt;p&gt;This is a good thing! It’ll lead to the continued democratization of programming and ever
more people in conversation with computers. But I don’t think this changes the fundamental
reality that the &lt;em&gt;best&lt;/em&gt; programmers aren’t the ones that make the widest use of the
highest abstractions. That’ll continue to be those who dig down and understand what’s
happening at a deeper level — that understanding will always lead to more deft use of any
tools that programmers have at their disposal.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;All &lt;a href=&quot;https://nick.groenen.me/posts/the-4-types-of-technical-documentation/&quot;&gt;four of types of
documentation&lt;/a&gt;
certainly existed before the advent of search engines, but shorter-form content thrives in
ranked searches and recommendation algorithms. &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;The ones filled with stacks and stacks of books, not the ones with pre-written
functions you can call from your programs &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><pubDate>Sat, 25 Oct 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing The Will of the Many by James Islington</title><link>https://davi.sh/reading/2025/will-of-the-many/</link><guid isPermaLink="true">https://davi.sh/reading/2025/will-of-the-many/</guid><description>&lt;p&gt;&lt;em&gt;The Will of the Many&lt;/em&gt; started off feeling like a cross between the political setting of
&lt;em&gt;Red Rising&lt;/em&gt; and the magic system of &lt;em&gt;The Fifth Season&lt;/em&gt;. The world felt big, but not
particularly filled in — the author throws out place names and countries, and we meet
characters from different corners of the now world-spanning Catenan Republic, but the
places don’t feel particularly differentiated or deep. A map would have helped here, but
maybe the author has a reason to hold one back.&lt;/p&gt;
&lt;p&gt;While the worldbuilding wasn’t as deep as what I’m normally interested in, I ultimately
found myself drawn in by the web of intrigue and competing political motications that the
narrator/protagonist finds himself surrounded by. He has no “correct” choices. He’s unsure
of his own goals beyond survival. And while he’s uncovering the secrets and motives of
those around him as he goes, it doesn’t feel like the suspense is just the drip-drip-drip
of information — no other character has all the pieces to the puzzle, either.&lt;/p&gt;
&lt;p&gt;The plot is messy in such a satisfying way. I’m not sure if the focus of the second
installment will continue to be on the same story or if it will shift its attention
elsewhere. But I’ll be excited to dive back in when it comes out.&lt;/p&gt;</description><pubDate>Thu, 18 Sep 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing The Telling by Ursula K. Le Guin</title><link>https://davi.sh/reading/2025/the-telling/</link><guid isPermaLink="true">https://davi.sh/reading/2025/the-telling/</guid><description>&lt;p&gt;Back to science fiction with another installment in the Hainish Cycle! &lt;em&gt;The Telling&lt;/em&gt; is
about orthodoxy, totalitarianism, and cultural revolution. It felt different than other
sci-fi I’ve read from Le Guin. Instead of being a man from an alient planet like
Hain-Davenant or Annares, the narrator is an Indian-Canadian woman from Earth. While the
story takes place on the recently-uplifted planet Aka, Earth and the narrator’s
rememberings and attitudes towards our home feature strongly in the book.&lt;/p&gt;
&lt;p&gt;The Hainish Cycle is all about celebrating how our differences make us stronger while
emphasizing our common humanity. Bringing Earth to the forefront of this installment
helped drive the lesson home in a new way.&lt;/p&gt;</description><pubDate>Fri, 15 Aug 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing The Farthest Shore by Ursula K. Le Guin</title><link>https://davi.sh/reading/2025/the-farthest-shore/</link><guid isPermaLink="true">https://davi.sh/reading/2025/the-farthest-shore/</guid><description>&lt;p&gt;Compared to the first two books in &lt;em&gt;Earthsea&lt;/em&gt; I was not as excited about &lt;em&gt;The Farthest
Shore&lt;/em&gt;. Some of the scenes felt — for lack of a better word — a bit trippy, which made
the narrative a bit harder to follow.&lt;/p&gt;
&lt;p&gt;Although I do understand that &lt;em&gt;Earthsea&lt;/em&gt; is marketed for a late-teen audience, I may be
getting a bit tired of the coming-of-age tropes. “The Farthest Shore” still continues to
fill out the world of &lt;em&gt;Earthsea&lt;/em&gt; in an interesting and satisfying way.&lt;/p&gt;</description><pubDate>Sun, 27 Jul 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing The Tombs of Atuan by Ursula K. Le Guin</title><link>https://davi.sh/reading/2025/tombs-of-atuan/</link><guid isPermaLink="true">https://davi.sh/reading/2025/tombs-of-atuan/</guid><description>&lt;p&gt;The second installment of the &lt;em&gt;Earthsea&lt;/em&gt; series, I enjoyed this book about as much as &lt;em&gt;A
Wizard of Earthsea&lt;/em&gt;. Le Guin puts a lot of effort into fleshing out the world of Earthsea,
but unlike other fantasy worlds, she’s doing it through the eyes of her characters rather
than through exposition or lore dumps.&lt;/p&gt;
&lt;p&gt;Ged, the protagonist of &lt;a href=&quot;../wizard-of-earthsea.md&quot;&gt;book one&lt;/a&gt;, and Arha, the protagonist of
this book, grew up in completely different circumstances. They see the same world and same
events through their own differing and often conflicting perspectives. While it may sound
disorienting, the overall world feels even more coherent and real for it.&lt;/p&gt;</description><pubDate>Fri, 25 Jul 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing A Wizard of Earthsea by Ursula K. Le Guin</title><link>https://davi.sh/reading/2025/wizard-of-earthsea/</link><guid isPermaLink="true">https://davi.sh/reading/2025/wizard-of-earthsea/</guid><description>&lt;p&gt;Until now I’ve only read Le Guin’s science fiction work, and have wanted to give her
fantasy series &lt;em&gt;Earthsea&lt;/em&gt; a try for a while.&lt;/p&gt;
&lt;p&gt;I really enjoyed this first installment. The societies that the protagonist ventures
through felt lived-in in a way that feels missing from some other high fantasy like the
Lord of the Rings.&lt;/p&gt;
&lt;p&gt;The way that magic and the sorcerers and witches who perform it are weaved into the world
was really well done. “Sorcerer” is a vocation like any other, although one that’s highly
valued in many places. Sorcerers and witches often fill in the role of high technology,
whether that’s powered vehicles or medicine.&lt;/p&gt;
&lt;p&gt;The magic system itself felt like it had its own internal logic, But enough mystery was
left for there to be places for Le Guin to fill in the blanks in future novels without
backing herself in a corner.&lt;/p&gt;
&lt;p&gt;Beyond the top-level genre, &lt;em&gt;A Wizard of Earthsea&lt;/em&gt; is a coming of age story, which are
always enjoyable in their own way. Even if the “twist” was a bit predictable, the
worldbuilding was top-notch, and I’m excited to pick up the second book in the series!&lt;/p&gt;</description><pubDate>Sun, 20 Jul 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing You Dreamed of Empires by Álvaro Enrigue</title><link>https://davi.sh/reading/2025/you-dreamed-of-empires/</link><guid isPermaLink="true">https://davi.sh/reading/2025/you-dreamed-of-empires/</guid><description>&lt;p&gt;A retelling of the initial encounter in Tenoxtitlan between the Aztecs and the Spanish. I
found it a bit hard to get into this book. Translations are always tough since translators
are rarely as skilled with words as the original authors and there’s two levels of
intention and meaning to work through.&lt;/p&gt;
&lt;p&gt;One thing I did enjoy was how Moctezuma and Cortés, both giant historical figures, were
brought down to human scale through their point-of-view chapters.&lt;/p&gt;
&lt;p&gt;Overall, I found the pacing quite slow and the climax somewhat rushed. I kind of think
that a sequel would be more interesting from a plot perspective — although I expect that
plot was not what Enrigue was optimizing for.&lt;/p&gt;</description><pubDate>Mon, 14 Jul 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing A Fisherman of the Inland Sea by Ursula K. Le Guin</title><link>https://davi.sh/reading/2025/fisherman/</link><guid isPermaLink="true">https://davi.sh/reading/2025/fisherman/</guid><description>&lt;p&gt;I read many of Le Guin’s more classic entries in her Hainish Cycle back in college and
since then have been fascinated with the world she built for novels like &lt;em&gt;The Disposessed&lt;/em&gt;
and &lt;em&gt;The Left Hand of Darkness&lt;/em&gt;. &lt;em&gt;A Fisherman of the Inland Sea&lt;/em&gt; is a series of short
stories of which only the last three have anything to do with the universe of the Hainish
Cycle. These three all revolve around the development of instantaneous, faster than light
travel that Le Guin calls “churten theory” — notably &lt;em&gt;not&lt;/em&gt; capitalized most of the
time. Le Guin doesn’t focus on the effects of FTL travel on the society of the
Ekumen. Instead, the imprecision of the new technology opens the door to stories about
love, community, and how the narratives we tell ourselves affect the way we move through
the world.&lt;/p&gt;
&lt;p&gt;These short stories weren’t as fully baked as Le Guin’s full-length novels, but in some
ways that helped the core insights behind them shine through even clearer.&lt;/p&gt;</description><pubDate>Sun, 25 May 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing Tress of the Emerald Sea by Brandon Sanderson</title><link>https://davi.sh/reading/2025/tress/</link><guid isPermaLink="true">https://davi.sh/reading/2025/tress/</guid><description>&lt;p&gt;Part fantasy, part pirate novel, part fairy tale, this was the first Brandon Sanderson
book I’ve read, and I’m not sure it was the best one to start on. The main things I
enjoyed in this book were the worldbuilding and the writing style. Unfortunately, I had a
hard time disentangling the character of the in-universe narrator with the author, who
took almost every opportunity to drop “wisdom” and moralize the current situation. The
story itself felt like it predictably flowed along the lines of fairy tale tropes even
when it was subverting them.&lt;/p&gt;
&lt;p&gt;Reading the acknowledgements section after finishing the book, it’s clear that Sanderson
was trying out a new first-person narrator and also targeting a “The Princess Bride”-style
fairy tale pretty explicitly here. I’ll probably try out another one of his novels
sometime soon to see if the things I did like about this book shine through some more.&lt;/p&gt;</description><pubDate>Mon, 12 May 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing Zen and the Art of Motorcycle Maintenance by Robert M. Pirsig</title><link>https://davi.sh/reading/2025/zen-motorcycle/</link><guid isPermaLink="true">https://davi.sh/reading/2025/zen-motorcycle/</guid><description>&lt;p&gt;I really enjoyed this book — its final lines even surpassed &lt;em&gt;The Great Gatsby&lt;/em&gt; as my
favorite closing lines of a novel. But I couldn’t find myself justifying a rating higher
than 4/5 when I broke it down and tried to explain what exactly I enjoyed so much in it. I
caught myself after a few minutes, smiled, and realized in some ways that’s what &lt;em&gt;Zen and
the Art of Motorcycle Maintenance&lt;/em&gt; was all about.&lt;/p&gt;
&lt;p&gt;I’m a very analytical person. I think that isn’t particularly surprising considering I
started programming computers way before my brain finished developing. While analytic,
dualistic, “dialectical” thinking has benefited me in a lot of ways, I’ve come to realize
over the past few years that not everything in life can or should be intellectualized or
broken down into repeatable understanding. &lt;em&gt;Zen and the Art of Motorcycle Maintenance&lt;/em&gt;
gave some form and vocabulary to a lot of things I’ve been thinking and sometimes writing
about over the past few years. I found a lot of resonance in how the book describes how
those of us with more analytical tendencies can widen our perspective.&lt;/p&gt;
&lt;p&gt;A description of a cross-country motorcycle trip unfolds alongside the narrator’s
philosophical musings to the reader, and the two threads intersect and complement each
other at various times throughout the book. The “present-day” story starts off slow but
picks up as the philosophising half (the “Chautauqua”) starts giving more depth back to
the characters in the story-half. At the end of some chapters, I found myself doing
something I’ve rarely, if ever, done before: I put the book down just to think through and
digest what I’d just read rather than powering full speed ahead to see what happens next.&lt;/p&gt;
&lt;p&gt;I can’t remember the last time I wanted to start a book over again as soon as I finished,
but I feel like this is a book that would really read differently the second time around
with the full context that’s revealed mid-way. I think I’ll just sit for a little longer
with that first impression, though.&lt;/p&gt;</description><pubDate>Wed, 07 May 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing Abundance by Ezra Klein and Derek Thompson</title><link>https://davi.sh/reading/2025/abundance/</link><guid isPermaLink="true">https://davi.sh/reading/2025/abundance/</guid><description>&lt;p&gt;I listen to both Ezra’s and Derek’s podcasts regularly and was pretty familiar with the
outline of “Abundance” the concept from Klein mulling over his thoughts over the past four
years on the Ezra Klein Show. I’m broadly sympathetic to the aims of the book but I felt
like &lt;em&gt;Abundance&lt;/em&gt; fell short of crafting the most convincing argument for “a liberalism
that builds.”&lt;/p&gt;
&lt;p&gt;I don’t know if the felt lack of insight from the book is because I listen to the authors
on a weekly basis or if the book was aimed at a wider audience who have been less exposed
to these ideas. Regardless, I wish they had focused a bit more attention on explaining why
an Abundance agenda will be difficult politics.&lt;/p&gt;
&lt;p&gt;The “Build” chapter glosses over safety regulation pretty quickly without engaging at all
with how workplace accidents can become media sensations or the real the tradeoffs that
would need to be made to make safety regulations less onerous.&lt;/p&gt;
&lt;p&gt;In “Invent”, much of the focus is on how the NIH and the American academic establishment
let Katalin Karikó and her research on mRNA vaccines slip through the cracks. While the
$450 million Stimulus/DoE loan that Tesla recieved is mentioned, the similar grant paid to
Solyndra before the company went bankrupt never is. Wanting government to be less risk
averse is a good aim that I support, but taking on risk and uncertainty &lt;em&gt;does&lt;/em&gt; lead to a
higher likelihood of undesirable outcomes. There’s a political communication problem in
helping the public understand that a single failed company in a startup program doesn’t
indicate waste or foul play.&lt;/p&gt;
&lt;p&gt;The specific DoE program that funded Tesla and Solyndra &lt;a href=&quot;https://archive.is/RpDqC&quot;&gt;made it into the black by
2015&lt;/a&gt;, but beyond the economics of this one program, the ways in
which it supports American businesses should be accounted for separately. Tesla very
likely wouldn’t be where it is today without that Stimulus loan, and I think that’s worth
at least half a dozen Solyndras.&lt;/p&gt;
&lt;p&gt;A specter hanging over the book is that the authors seem to be advocating for something
that on the surface has the same goals as Elon Musk and Vivek Ramaswamy’s Department of
Government Effeciency had when it was announced after the election. This book is both
timely and ill-timed — the authors have had to spend a lot of time in interviews
distancing themselves from DOGE as Musk has “deleted” federal programs and agencies left
and right. I worry that any effective implementation of Abundance on the national level
will be held back by negative associations with the ramifications of this push for
“effeciency” that the country will be feeling for years go come.&lt;/p&gt;
&lt;p&gt;I think Abundance is still worth reading, if only to understand its place in the recent
discourse, and especially if you’re not particularly familiar with the authors or topic
areas. The book is more of the beginning of the discussion rather than the final word.&lt;/p&gt;</description><pubDate>Sun, 13 Apr 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing Golden Son by Pierce Brown</title><link>https://davi.sh/reading/2025/golden-son/</link><guid isPermaLink="true">https://davi.sh/reading/2025/golden-son/</guid><description>&lt;p&gt;I found Golden Son to be even more of a page-turner than &lt;a href=&quot;red-rising&quot;&gt;Red Rising&lt;/a&gt;. The
setting of the book changes pretty significantly and the cast of characters around the
protagonist mostly shifts as well, but I was kept interested by how the author continues
to build out the world of the series.&lt;/p&gt;
&lt;p&gt;What brings my review down a star is how Brown moves from relying on themes and ideas from
other sci-fi series to borrowing some pretty major plot points. The way that this second
book leans more in its influences than the first has made me less excited to continue the
series from here — I started book three, but I put it down a quarter of the way through, and
I’m not feeling much motivation to pick it back up.&lt;/p&gt;</description><pubDate>Sun, 16 Mar 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing Red Rising by Pierce Brown</title><link>https://davi.sh/reading/2025/red-rising/</link><guid isPermaLink="true">https://davi.sh/reading/2025/red-rising/</guid><description>&lt;p&gt;A fun sci-fi novel that brought together a lot of parts of other series and franchises
into an interesting world: a solar-system spanning oligarchy with a caste system backed by
geenric engineering whose ruling class reveres the ancient Romans more than any
enlightenment-era political theorist.&lt;/p&gt;
&lt;p&gt;Some parts will stick out as a bit derivative: the main setting of the book is a Hunger
Games-esque battle royale, and the strategies and political theory the main characters use
to win felt very reminiscent of Ender’s Game. Even so, the book was able to weave in its
own themes. I found the narrator’s internal struggles interesting to follow along with and
generally the book was a page turner.&lt;/p&gt;</description><pubDate>Sun, 02 Mar 2025 17:00:00 GMT</pubDate></item><item><title>2024 in review</title><link>https://davi.sh/blog/2024/12/2024-in-review/</link><guid isPermaLink="true">https://davi.sh/blog/2024/12/2024-in-review/</guid><description>&lt;p&gt;I wrote most of this post during the week between Christmas Eve and New Year’s Day when
enough people in New York have left the city that it makes it quite difficult &lt;em&gt;not&lt;/em&gt; to
turn introspective. It’s been a pretty big year for me and for this blog, so I thought it
would be fun to take a look back on the year as we all move into the second half of the
decade.&lt;/p&gt;
&lt;h3 id=&quot;blog-posts&quot;&gt;Blog Posts&lt;/h3&gt;
&lt;p&gt;I wrote four long-form blog posts this year: &lt;a href=&quot;/blog/2024/04/dune&quot;&gt;a fun post on Dune&lt;/a&gt; for
&lt;a href=&quot;https://www.aprilcools.club&quot;&gt;April Cools&lt;/a&gt; and three in my &lt;a href=&quot;/blog/tags/nix-on-mac/&quot;&gt;Nix on
macOS&lt;/a&gt; series. I’ve gotten a ton of really encouraging feedback
from friends, acquaintances and internet strangers about my Nix articles.&lt;/p&gt;
&lt;p&gt;Somehow &lt;a href=&quot;/til/nix/nix-macos-setup/&quot;&gt;my TIL post&lt;/a&gt; from November 2023 started ranking well
on Google and my site is one of the first that shows up when most folks search for “nix
darwin”. I’m proud my writing has been useful to folks. It’s a good reminder that I can
find an audience in a niche just writing about what I want to write about before worrying
about metrics at all. I still have at least three more posts to write from my original
series outline, but we’ll see if I switch tach and write about other things in 2025.&lt;/p&gt;
&lt;h3 id=&quot;the-newsletter&quot;&gt;The Newsletter&lt;/h3&gt;
&lt;p&gt;One of my goals for 2024 was to write more. I’m not always motivated to write deeper
long-form technical content that I’m happy to put up on the main blog, though, so I took a
page out of &lt;a href=&quot;https://arne.me&quot;&gt;Arne Bahlo&lt;/a&gt;’s playbook and started &lt;a href=&quot;/weekly&quot;&gt;a link curation
newsletter&lt;/a&gt;. It’s a genre that provides structure while still gives me space to
express myself, share something interesting, and flex my writing muscles some more.&lt;/p&gt;
&lt;p&gt;My blurbs about each link follow a bit more from &lt;a href=&quot;https://simonwillison.net&quot;&gt;Simon
Willison&lt;/a&gt; or &lt;a href=&quot;https://daringfireball.net&quot;&gt;John Gruber&lt;/a&gt;’s link
blogs than Arne’s summaries — I try to add some analysis and sprinkle in nuance where I
can. The original goal was to expand some of these blurbs into full blog posts. I
haven’t felt the burning desire to do that yet, but we’ll see how next year goes.&lt;/p&gt;
&lt;p&gt;The newsletter is called Weekly Links, and after a 52 weeks there’s… well.. 17
entries. The first 10 came quickly and easily, but I started falling off with the habit in
early March and ultimately had a six month gap between #12 in early May and lucky #13 in
November. I don’t want to beat myself up about not following through with the weekly
cadence — I want to make sure I’m writing these for myself first and foremost, and I’ve
really been enjoying getting back into the habit of writing.&lt;/p&gt;
&lt;h3 id=&quot;what-i-learned&quot;&gt;What I Learned&lt;/h3&gt;
&lt;p&gt;I barely had enough articles saved up to fill up my first newletter back from hiatus in
November. When I sat down to write #14, though, I had more links saved up from the week
than I could fit in one post. It’s been a great example of how writing for me is a
&lt;em&gt;&lt;strong&gt;positive sum&lt;/strong&gt;&lt;/em&gt; activity — the more I write, the more my mind is attuned to finding
interesting things to write about.&lt;/p&gt;
&lt;p&gt;Discovering positive-sum feedback loops has been the real theme of the year for
me. Whether it’s writing each week, running a mile in the morning, or working on
interesting projects at work, I’ve discovered first-hand how activity begets energy when
you’re working on the right things. I’ve struggled with motivation and energy levels in
the past, but this year I’ve learned that sometimes you just need to kickstart the
flywheel.&lt;/p&gt;
&lt;p&gt;In that vein, the article that’s had the greatest impact on me this year is probably &lt;a href=&quot;https://macwright.com/2024/01/28/work-hard-and-take-everything-seriously.html&quot;&gt;Work hard and take everything really
seriously&lt;/a&gt;
by &lt;a href=&quot;https://macwright.com/&quot;&gt;Tom MacWright&lt;/a&gt; (featured in &lt;a href=&quot;/weekly/2024-02-04/&quot;&gt;Weekly
#6&lt;/a&gt;). It helped me crystallize how I’d been feeling around work and
leisure time — how “balance” is more complicated than we often give it credit for.&lt;/p&gt;
&lt;p&gt;All of these habits may be self-reinforcing in some ways, but they also require active
maintenance. My main goal for next year is to do the things that excite me more often.&lt;/p&gt;
&lt;p&gt;As always, thanks for reading! I hope everyone had a great new year, and I’m excited to
see where 2025 leads.&lt;/p&gt;</description><pubDate>Sat, 04 Jan 2025 17:00:00 GMT</pubDate></item><item><title>Reviewing Tomorrow, and Tomorrow, and Tomorrow by Gabrielle Zevin</title><link>https://davi.sh/reading/2024/tomorrow3/</link><guid isPermaLink="true">https://davi.sh/reading/2024/tomorrow3/</guid><description>&lt;p&gt;A book about life, love, and video games. I honestly didn’t know what I was getting into
when I started this book, but by the time they described the twist behind Sadie’s first
video game in the second chapter I was hooked. Some people may be put off by how the
characters and the third-person narrator talk about video games as a high art form but I
love hearing and seeing how digital media can expand our artistic horizons rather than
shrink them.&lt;/p&gt;
&lt;p&gt;A friend who read the book around the same time described it as “a romance, I guess?” and
that ambiguity is at the heart of what makes this book feel real. As we follow the main
characters from their college days into their thirties, the ways their views about the
world and each other change does a lot towards making the characters feel like real
people.&lt;/p&gt;
&lt;p&gt;Ultimately I got a lot out of what this book had to say about friendship. It’s natural for
miscommunications to accumulate into misunderstandings over the years despite our best
intentions. What’s important in the end is to remember that it’s never too late to return
to the foundation and try to grow something new.&lt;/p&gt;</description><pubDate>Fri, 20 Dec 2024 17:00:00 GMT</pubDate></item><item><title>[TIL] nix flake update: Updating dependencies of a Nix flake</title><link>https://davi.sh/til/nix/flake-update/</link><guid isPermaLink="true">https://davi.sh/til/nix/flake-update/</guid><description>&lt;p&gt;I just realized today that I haven’t updated VSCode on my Mac in almost a year. It turns
out that if you install any program through Nix, its version is determined by the
version of nixpkgs in your &lt;code&gt;flake.lock&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;To update nixpkgs and get newer versions of all your programs, &lt;code&gt;cd&lt;/code&gt; into the directory
with your flake and run &lt;code&gt;nix flake update&lt;/code&gt;. It’s the &lt;code&gt;pacman -Syu&lt;/code&gt; of nix!&lt;/p&gt;</description><pubDate>Sat, 30 Nov 2024 17:00:00 GMT</pubDate></item><item><title>Configuring VSCode with Nix on macOS</title><link>https://davi.sh/blog/2024/11/nix-vscode/</link><guid isPermaLink="true">https://davi.sh/blog/2024/11/nix-vscode/</guid><description>&lt;p&gt;Welcome back to the Nix on Mac series! By the end of this post, you’ll be able to fully
configure VSCode through your Nix flake via &lt;code&gt;home-manager&lt;/code&gt;, which we set up in &lt;a href=&quot;/blog/2024/02/nix-home-manager/&quot;&gt;part
2&lt;/a&gt;. This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Custom keybindings and settings&lt;/li&gt;
&lt;li&gt;Installing themes and extensions from a nixpkgs overlay&lt;/li&gt;
&lt;li&gt;Properly aliasing VSCode and other macOS applications to &lt;code&gt;/Applications&lt;/code&gt; for Spotlight&lt;/li&gt;
&lt;/ul&gt;
&lt;!--more--&gt;
&lt;p&gt;Without further ado, let’s dive in!&lt;/p&gt;
&lt;h2 id=&quot;installing-vscode&quot;&gt;Installing VSCode&lt;/h2&gt;
&lt;p&gt;Nix won’t install non-free software like VSCode without you opting in. Add this line
to your &lt;code&gt;nix-darwin&lt;/code&gt; &lt;code&gt;configuration&lt;/code&gt; module:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;configuration&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ... &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;allowUnfree&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, installing VSCode is just one line of code in our &lt;code&gt;home-manager&lt;/code&gt; config:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;homeconfig&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ... &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    programs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;vscode&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’m sure many of you who are following along are using VSCode to edit your &lt;code&gt;flake.nix&lt;/code&gt;
file. I should note that installing VSCode through &lt;code&gt;home-manager&lt;/code&gt; will likely blow away
your existing settings when you call &lt;code&gt;switch&lt;/code&gt;. Now would be a good time to back up your
existing settings and extension list so you can replicate your setup from within Nix.&lt;/p&gt;
&lt;p&gt;VSCode will be installed in &lt;code&gt;/Users/$USER/Applications/Home Manager Apps/&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; after
running &lt;code&gt;switch&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you’ve previously installed VSCode, go ahead and dump the copy from &lt;code&gt;/Applications&lt;/code&gt; in
the trash. &lt;a href=&quot;https://media1.tenor.com/m/3idC48k28zcAAAAd/roads-where-were-going-we-dont-need-roads.gif&quot;&gt;Where we’re
going&lt;/a&gt;,
we don’t need &lt;del&gt;roads&lt;/del&gt; globally installed programs!&lt;/p&gt;
&lt;h2 id=&quot;editor-settings-and-keybindings&quot;&gt;Editor Settings and Keybindings&lt;/h2&gt;
&lt;p&gt;You can add user settings and keybindings pretty easily:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;programs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;vscode&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    userSettings&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # This property will be used to generate settings.json:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # https://code.visualstudio.com/docs/getstarted/settings#_settingsjson&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &quot;editor.formatOnSave&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    keybindings&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # See https://code.visualstudio.com/docs/getstarted/keybindings#_advanced-customization&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            key&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;shift+cmd+j&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            command&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;workbench.action.focusActiveEditorGroup&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            when&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;terminalFocus&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It almost looks like JSON! And in fact, converting Nix types to and from JSON is &lt;a href=&quot;https://nixos.org/manual/nix/stable/language/builtins.html#builtins-toJSON&quot;&gt;part of
the Nix standard
library&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;extensions&quot;&gt;Extensions&lt;/h2&gt;
&lt;p&gt;A huge part of VSCode is the bountiful extension ecosystem. &lt;code&gt;home-manager&lt;/code&gt; lets you
install extensions similarly to how it installs packages. The full &lt;a href=&quot;https://marketplace.visualstudio.com/vscode&quot;&gt;VSCode
marketplace&lt;/a&gt; isn’t present in nixpkgs by
default, so we’ll need to install an overlay.&lt;/p&gt;
&lt;p&gt;Nixpkgs overlays let you override and add new entries to nixpkgs. We can add the
&lt;a href=&quot;https://github.com/nix-community/nix-vscode-extensions&quot;&gt;&lt;code&gt;nix-vscode-extensions&lt;/code&gt;&lt;/a&gt; overlay
by adding a line to our &lt;code&gt;nix-darwin&lt;/code&gt; configuration:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  inputs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    nix-vscode-extensions&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;github:nix-community/nix-vscode-extensions&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  outputs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    inputs@{ self&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; nixpkgs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; nix-darwin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; home-manager&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mac-app-util&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; nix-vscode-extensions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    let&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      configuration&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ... &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;overlays&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;          nix-vscode-extensions&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;overlays&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      homeconfig&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        programs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;vscode&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;          # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;          userSettings&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &quot;workbench.colorTheme&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Dracula Theme&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;          # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;          extensions&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;            pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;vscode-marketplace&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;jnoortheen&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;nix-ide&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;            pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;vscode-marketplace&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;dracula-theme&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;theme-dracula&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;vscode-marketplace&lt;/code&gt; is one of the properties that the &lt;code&gt;nix-vscode-extensions&lt;/code&gt; overlay
added to nixpkgs.  Any VSCode extension in the marketplace should be accessible from
&lt;code&gt;pkgs.vscode-marketplace.$AUTHOR.$EXTENSION&lt;/code&gt;, where &lt;code&gt;$AUTHOR.$EXTENSION&lt;/code&gt; is the same as
the &lt;code&gt;itemName&lt;/code&gt; property in the extension’s URL on the &lt;a href=&quot;https://marketplace.visualstudio.com/vscode&quot;&gt;extension marketplace
website&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;cleaning-up-lists-of-attributes-with-the-with-clause&quot;&gt;Cleaning up lists of attributes with the &lt;code&gt;with&lt;/code&gt; clause&lt;/h3&gt;
&lt;p&gt;Writing &lt;code&gt;pkgs.vscode-marketplace&lt;/code&gt; in front of every extension will get tedious as as your
list of extenions gets longer and make the list harder to read. Luckily Nix has a language
construct to help with this: in front of any expression, you can type &lt;code&gt;with &amp;#x3C;attribute set&gt;&lt;/code&gt; to bring all attributes within the attribute set into scope. Our &lt;code&gt;extensions&lt;/code&gt; list
can look like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;extensions&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;vscode-marketplace&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    jnoortheen&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;nix-ide&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    dracula-theme&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;theme-dracula&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Much cleaner!&lt;/p&gt;
&lt;h2 id=&quot;playing-nice-with-spotlight&quot;&gt;Playing nice with Spotlight&lt;/h2&gt;
&lt;p&gt;You might have noticed that the version of VSCode that Nix installs doesn’t show up in
Spotlight. Why is that? It’s unfortunately pretty simple: All artifacts that Nix and
&lt;code&gt;home-manager&lt;/code&gt; add to your system are symbolic links, and Spotlight won’t index
symlinks. There’s &lt;a href=&quot;https://github.com/nix-community/home-manager/issues/1341&quot;&gt;a very long thread about this on
GitHub&lt;/a&gt; if you’re interested in
reading. From the various options discussed there,
&lt;a href=&quot;https://github.com/hraban/mac-app-util&quot;&gt;&lt;code&gt;mac-app-util&lt;/code&gt;&lt;/a&gt; is the easiest way to get
Spotlight working as expected.&lt;/p&gt;
&lt;p&gt;We can add it as another input at the top of our flake and then modify our flake output
to load &lt;code&gt;mac-app-util&lt;/code&gt; in both &lt;code&gt;nix-darwin&lt;/code&gt; and &lt;code&gt;home-manager&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    description&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;My system configuration&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    inputs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        mac-app-util&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;github:hraban/mac-app-util&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;    in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      darwinConfigurations&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;$HOSTNAME&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; nix-darwin&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;lib&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;darwinSystem&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        modules&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;          # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;          mac-app-util&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;darwinModules&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;          home-manager&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;darwinModules&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;home-manager&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;sharedModules&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;                mac-app-util&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;homeManagerModules&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running &lt;code&gt;switch&lt;/code&gt;, VSCode should be properly copied to
&lt;code&gt;/Users/$USER/Applications/Home Manager Trampolines&lt;/code&gt; and should be available the next
time Spotlight refreshes.&lt;/p&gt;
&lt;h3 id=&quot;stepping-back&quot;&gt;Stepping Back&lt;/h3&gt;
&lt;p&gt;We just included someone’s third-party Nix utility in our flake to do some custom behavior
when Nix installs macOS GUI apps. You probably didn’t notice that &lt;code&gt;mac-app-util&lt;/code&gt; is
written in &lt;strong&gt;Common Lisp&lt;/strong&gt;. I’ve never written Common Lisp, and my guess is you haven’t
either. Even so, with fewer than five lines of code we were able make use of some code that
someone wrote in their favorite niche programming language without needing to figure out
how to build or run that code. Nix handled it all for us! I, for one, think that’s pretty
amazing.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Our Nix config is now able to fully configure and manage VSCode, including installing
extensions and themes. &lt;code&gt;home-manager&lt;/code&gt; has just as deep an integration with many other
utilities. If you haven’t already definitely go through and check out &lt;a href=&quot;https://nix-community.github.io/home-manager/options.xhtml&quot;&gt;home-manager’s
config options&lt;/a&gt; to find out
what you can do with your favorite program.&lt;/p&gt;
&lt;p&gt;You can find the full &lt;code&gt;flake.nix&lt;/code&gt; for this installment on GitHub
&lt;a href=&quot;https://github.com/davish/nix-on-mac/tree/part-3&quot;&gt;here&lt;/a&gt;. See you in the next one!&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Notably, this isn’t inside the normal &lt;code&gt;/Applications&lt;/code&gt; folder since &lt;code&gt;home-manager&lt;/code&gt;
can only install programs under your home directory. Even stranger, Spotlight can’t
pick up our alias! We’ll come back and fix this at the end of the post. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><pubDate>Sun, 24 Nov 2024 17:00:00 GMT</pubDate></item><item><title>The Spice Didn&apos;t Always Flow</title><link>https://davi.sh/blog/2024/04/dune/</link><guid isPermaLink="true">https://davi.sh/blog/2024/04/dune/</guid><description>&lt;p&gt;Dune: Part Two hit theaters recently, which got me thinking about my own experience reading
Dune in the past, what I enjoyed about it, and my own personal headcannon that made this
novel more enjoyable for me. And hey, it’s &lt;a href=&quot;https://www.aprilcools.club&quot;&gt;April Cools&lt;/a&gt;, so
why not put pen to paper here?&lt;/p&gt;
&lt;p&gt;Credit to &lt;a href=&quot;https://reddit.com/r/dune/comments/ddu3cu/who_controlled_arrakis_before_the_harkonnens/f2oe959/&quot;&gt;this reddit
thread&lt;/a&gt;
for the initial discussion, but I hope I’ve expanded on the concept below.&lt;/p&gt;
&lt;h2 id=&quot;questions&quot;&gt;Questions&lt;/h2&gt;
&lt;p&gt;I’ve read Dune a few times over the past ten years, and as immersive as the world and
politics are, a few discontinuities kept bothering me.&lt;/p&gt;
&lt;h3 id=&quot;the-universes-best-kept-secret&quot;&gt;The universe’s best-kept secret&lt;/h3&gt;
&lt;p&gt;Spice is commonly referred to as a geriatric drug that slows down the effects of aging
before Paul escapes to the desert, and this is the explanation given to the reader and to
Paul as to why it’s so valuable a commodity. In the final chapter, it’s considered a big
reveal that the Guild Navigators are addicted to the spice, and have the Eyes of Ibad just
like the Fremen, carefully concealed by colored contact lenses even in front of the
Emperor:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The taller of the two, though, held a hand to his left eye. As the Emperor watched,
someone jostled the Guildsman’s arm, the hand moved, and the eye was revealed. The man
had lost one of his masking contact lenses, and the eye stared out a total blue so dark
as to be almost black. (Book 3, Chapter 48)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the Spice has been around for thousands of years, how is it still not common knowledge
among at least the Great Houses that spice induces hallucinations and limited prescience?&lt;/p&gt;
&lt;h3 id=&quot;when-was-spice-discovered&quot;&gt;When was spice discovered?&lt;/h3&gt;
&lt;p&gt;Melange is described as essential to space travel, and yet the narrator and many
characters talk about the time of its discovery as if it was close to within living
memory.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;He thought of the filmbook Yueh had shown him — “Arrakis: His Imperial Majesty’s
Desert Botanical Testing Station.” It was an old filmbook from before the discovery of
the spice. (Book One, Chapter 9)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Beyond the logistics of a filmbook being preserved for over 10 millennia, it’s described
as “old” rather than “ancient”, the preferred word used throughout Dune to describe the
incomprehensible age of structures like Castle Caladan and languages like Chakobsa.&lt;/p&gt;
&lt;p&gt;Evidence of a human presence on Arrakis before the discovery of the spice, but after the
establishment of the Empire in the wake Butlerian Jihad, brings up another question: how
did humans even make it to Arrakis without spice in the first place?&lt;/p&gt;
&lt;h2 id=&quot;the-theory&quot;&gt;The Theory&lt;/h2&gt;
&lt;p&gt;My own personal headcannon does in fact contradict the later books, but makes
Dune as a standalone story more consistent to me. Rather than being an ancient part of the
traditions of the Fremen, the Spacing Guild, and the Bene Gesserit, &lt;strong&gt;Spice is a
relatively recent discovery by the Empire&lt;/strong&gt;, probably in the last hundred years or so.&lt;/p&gt;
&lt;p&gt;Arrakis itself was discovered at some point in the more distant past. It remained mostly
an ecological and scientific oddity rather than a center of commerce and resource
extraction. The Fremen’s ancestors were expelled from their previous home at some point in
the past and discovered the spice soon after arriving on Arrakis integrating it into their
spiritual rituals and replacing &lt;em&gt;other&lt;/em&gt; awareness-spectrum narcotics. As much is described
by Jessica after she becomes a Reverend Mother of the Fremen:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;… [T]he Fremen culture was far older than she had suspected.&lt;/p&gt;
&lt;p&gt;There had been Fremen on Poritrin, she saw, a people grown soft with an easy planet,
fair game for Imperial raiders to harvest and plant human colonies on Bela Tegeuse and
Salusa Secundus…&lt;/p&gt;
&lt;p&gt;And she saw the thread of the past carried by Sayyadina after Sayyadina — first by word
of mouth, hidden in the sand chanteys, then refined through their own Reverend Mothers
with the discovery of the poison drug on Rossak… and now developed to subtle strength
on Arrakis in the discovery of the Water of Life. (Book Two, Chapter 37)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Spice is just &lt;strong&gt;one&lt;/strong&gt; awareness-spectrum narcotic in Dune. What makes it special and
valuable is its potency and lack of side effects. Especially notable is its addictive
properties. Once you use the spice, any other drug fail to have the same
awareness-expanding effect. Paul’s lecture to Gaius Helen Mohaim and the Spacing Guild
representatives at the end of the book makes way more sense in this context:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[The Spacing Guild] might have taken Arrakis when they realized their error of
specializing on the melange awareness-spectrum narcotic for their navigators. (Book 3,
Chapter 48)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And pages later:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Even your Bene Gesserit Truthsayer is trembling,” Paul said. “There are other poisons
the Reverend Mothers can use for their tricks, but once they’ve used the spice liquor,
the others no longer work.” (Book 3, Chapter 48)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Considering the life-extending properties of spice and large-scale extraction only
beginning 80 years before, it’s likely that Mohaim and the Guild navigators or others in
their generation were likely the ones who made the decision to go all-in on Spice and
Arrakis, rather than Paul admonishing their predecessors thousands of years in the past.&lt;/p&gt;
&lt;p&gt;While it’s not really consistent with later books in the series, I feel like this
retcon/headcannon gives the characters of Dune a lot more agency in the history of their
world, and makes the Imperium feel like a more dynamic human society rather than ossified
in a single pattern for millennia.&lt;/p&gt;</description><pubDate>Mon, 01 Apr 2024 17:00:00 GMT</pubDate></item><item><title>Managing dotfiles on macOS with Nix</title><link>https://davi.sh/blog/2024/02/nix-home-manager/</link><guid isPermaLink="true">https://davi.sh/blog/2024/02/nix-home-manager/</guid><description>&lt;p&gt;In &lt;a href=&quot;/blog/2024/01/nix-darwin/&quot;&gt;part one&lt;/a&gt; of &lt;a href=&quot;/blog/tags/nix-on-mac/&quot;&gt;this series&lt;/a&gt; we
installed Nix, set up our system configuration with &lt;code&gt;nix-darwin&lt;/code&gt;, and installed some
packages at the system level. In this post, we’ll set up
&lt;a href=&quot;https://github.com/nix-community/home-manager&quot;&gt;&lt;code&gt;home-manager&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Unlike &lt;code&gt;nix-darwin&lt;/code&gt;, &lt;code&gt;home-manager&lt;/code&gt; is cross-platform: it works across NixOS, macOS, and
anywhere else Nix can be installed. It was difficult at first for me to understand how
&lt;code&gt;home-manager&lt;/code&gt; and &lt;code&gt;nix-darwin&lt;/code&gt; should interact. While there is definitely overlap with
what these two Nix libraries can do, &lt;code&gt;nix-darwin&lt;/code&gt; is used for managing system-wide
settings and applications: it brings the power of NixOS to the Mac. &lt;code&gt;home-manager&lt;/code&gt; on the
other hand is most useful for managing user-level configuration and dotfiles.&lt;/p&gt;
&lt;p&gt;By the end of this post we’ll have installed &lt;code&gt;home-manager&lt;/code&gt; and used it to set up
configuration for vim, zsh, and git.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id=&quot;install-home-manager&quot;&gt;Install &lt;code&gt;home-manager&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;home-manager&lt;/code&gt; will be another input to our flake. In our &lt;code&gt;inputs&lt;/code&gt; attribute, add
&lt;code&gt;home-manager&lt;/code&gt; under &lt;code&gt;nix-darwin&lt;/code&gt; in &lt;code&gt;flake.nix&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    description&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;My system configuration&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    inputs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;github:NixOS/nixpkgs/nixpkgs-unstable&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        nix-darwin&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;github:LnL7/nix-darwin&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            inputs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;follows&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;nixpkgs&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        home-manager&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;github:nix-community/home-manager&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            inputs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;follows&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;nixpkgs&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will then add the minimal home-manager config as another module under &lt;code&gt;configuration&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    outputs&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; inputs@{ self&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; nix-darwin&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; home-manager }:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    let&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        configuration&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ... &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # ... nix-darwin configuration from part 1 &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # remains unchanged here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        homeconfig&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # this is internal compatibility configuration &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # for home-manager, don&apos;t change this!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            home&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stateVersion&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;23.05&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # Let home-manager install and manage itself.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            programs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            home&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;packages&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; pkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            home&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;sessionVariables&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;                EDITOR&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;vim&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    darwinConfigurations&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;$HOSTNAME&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; nix-darwin&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;lib&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;darwinSystem&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        modules&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;            configuration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;            home-manager&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;darwinModules&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;                home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useGlobalPkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;                home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useUserPackages&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;                home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;verbose&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;                home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;users&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;USER&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; homeconfig&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our &lt;code&gt;darwinSystem&lt;/code&gt; setup now includes another module that bridges together &lt;code&gt;home-manager&lt;/code&gt;
and &lt;code&gt;nix-darwin&lt;/code&gt;. Remember to replace &lt;code&gt;$USER&lt;/code&gt; with your system user!&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;darwin-rebuild switch --flake ~/.config/nix&lt;/code&gt; to ensure everything is set up
correctly.&lt;/p&gt;
&lt;h2 id=&quot;managing-vimrc&quot;&gt;Managing &lt;code&gt;.vimrc&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Let’s manage our first dotfile! First, we’ll create a new file in our repository and add
it to git so that Nix can recognize it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ~/.config/nix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;set number&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; vim_configuration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; vim_configuration&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since this isn’t a vim configuration series we’ll just have a small config that will
demonstrate how &lt;code&gt;home-manager&lt;/code&gt; manages dotfiles. You should be able to see line numbers
when running vim after this configuration is applied.&lt;/p&gt;
&lt;p&gt;Inside our &lt;code&gt;homeconfig&lt;/code&gt; block before the closing &lt;code&gt;};&lt;/code&gt;, add this line:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;home&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;.vimrc&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;source&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ./vim_configuration&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This one line highlights a few subtleties of the Nix language. Let’s dig in a bit before
moving on.&lt;/p&gt;
&lt;h3 id=&quot;paths&quot;&gt;Paths&lt;/h3&gt;
&lt;p&gt;In Nix, unlike most languages, &lt;a href=&quot;https://nixos.org/manual/nix/stable/language/values#type-path&quot;&gt;Paths are first class primitive
values&lt;/a&gt; that are not inside
of quotes and start with &lt;code&gt;./&lt;/code&gt; for paths relative to the current file or &lt;code&gt;/&lt;/code&gt; for absolute
paths. &lt;code&gt;./vim_configuration&lt;/code&gt; points to the file that we created in the same directory as
&lt;code&gt;flake.nix&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You’ll run into type errors if you try passing strings to options where paths
are expected.&lt;/p&gt;
&lt;h3 id=&quot;setting-attributes-in-nix&quot;&gt;Setting attributes in Nix&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://nixos.org/manual/nix/stable/language/values#attribute-set&quot;&gt;Attribute sets&lt;/a&gt; are
probably the most ubiquitious datatype in Nix. They’re the equivalent of dictionaries in
other languages. Something that’s pretty unique to Nix is that setting nested attributes
are supported with some clever syntactic sugar. Desugaring our vim dotfile line above, we
would get:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;home&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    file&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &quot;.vimrc&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            source&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ./vim_configuration&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This shorthand is useful when our attribute sets are sparse, and they can be combined with
the “full” attribute set literal along the path. If we had two dotfiles, this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;home&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;.vimrc&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;source&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ./vim_configuration&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;home&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;.bash_profile&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;source&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ./bash_configuration&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Is equivalent to this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;home&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &quot;.vimrc&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;source&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ./vim_configuration&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &quot;.bash_profile&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;source&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ./bash_configuration&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are other options to pass to dotfiles besides &lt;code&gt;source&lt;/code&gt; like &lt;code&gt;onChange&lt;/code&gt; and
&lt;code&gt;recursive&lt;/code&gt;. See the &lt;a href=&quot;https://nix-community.github.io/home-manager/options.xhtml#opt-home.file&quot;&gt;home-manager
documentation&lt;/a&gt;
for more details.&lt;/p&gt;
&lt;p&gt;Once you run &lt;code&gt;darwin-rebuild switch --flake ~/.config/nix&lt;/code&gt;, run &lt;code&gt;vim ~/.config/nix/flake.nix&lt;/code&gt;. You should see line numbers in the left gutter:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  1 {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  2   description = &quot;My system configuration&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  3 &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  4   inputs = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  5     nixpkgs.url = &quot;github:NixOS/nixpkgs/nixpkgs-unstable&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  6     nix-darwin = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  7         url = &quot;github:LnL7/nix-darwin&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  8         inputs.nixpkgs.follows = &quot;nixpkgs&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  9     };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; 10     home-manager = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; 11         url = &quot;github:nix-community/home-manager&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; 12         inputs.nixpkgs.follows = &quot;nixpkgs&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; 13     };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; 14   };&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not the most exciting change, but we’ve configured an application using &lt;code&gt;home-manager&lt;/code&gt;!
You can exit vim by hitting &lt;code&gt;ESC&lt;/code&gt; followed by &lt;code&gt;:q!&amp;#x3C;enter&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;configuring-zsh-with-a-home-manager-dsl&quot;&gt;Configuring zsh with a &lt;code&gt;home-manager&lt;/code&gt; DSL&lt;/h2&gt;
&lt;p&gt;You’re probably getting tired of typing out or copy-pasting &lt;code&gt;darwin-rebuild switch --flake ~/.config/nix&lt;/code&gt; all the time. Now, let’s add a shell alias! Our fingers will thank us.&lt;/p&gt;
&lt;p&gt;We could create a &lt;code&gt;zsh_configuration&lt;/code&gt; file and use &lt;code&gt;home.file.&quot;.zshrc&quot;&lt;/code&gt; to symlink it to
the right spot, but I want to demonstrate how to use some of the built-in configuration
that &lt;code&gt;home-manager&lt;/code&gt; provides. Add the following inside our &lt;code&gt;homeconfig&lt;/code&gt; block:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;programs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;zsh&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    shellAliases&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        switch&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;darwin-rebuild switch --flake ~/.config/nix&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running &lt;code&gt;darwin-rebuild switch --flake ~/.config/nix&lt;/code&gt; for the last time, you’ll be
able to just run &lt;code&gt;switch&lt;/code&gt; from now on.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;programs.zsh.enable&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;home-manager&lt;/code&gt; will install zsh to your PATH and use
the different options under the attribute set to generate a &lt;code&gt;.zshrc&lt;/code&gt; for you. If you
already have a &lt;code&gt;.zshrc&lt;/code&gt;, Nix will ask you to move the file so it’s not overwritten.&lt;/p&gt;
&lt;h1 id=&quot;configuring-git&quot;&gt;Configuring git&lt;/h1&gt;
&lt;p&gt;Let’s add another stanza for configuring git with some common options while we’re
here. Make sure to put in your name and email address:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;programs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    userName&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;$FIRSTNAME $LASTNAME&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    userEmail&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;me@example.com&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    ignores&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;.DS_Store&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    extraConfig&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        init&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;defaultBranch&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;main&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        push&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;autoSetupRemote&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;installing-packages-with-home-manager-vs-nix-darwin&quot;&gt;Installing packages with &lt;code&gt;home-manager&lt;/code&gt; vs &lt;code&gt;nix-darwin&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;When should you prefer to use &lt;code&gt;home-manager&lt;/code&gt; versus &lt;code&gt;nix-darwin&lt;/code&gt;? This is really a matter
of opinion, but it’s important to keep in mind that former is cross-platform while
latter is locked to macOS.&lt;/p&gt;
&lt;p&gt;I generally default to using &lt;code&gt;home-manager&lt;/code&gt; for configuring anything that is not
macOS-specific. All your &lt;code&gt;home-manager&lt;/code&gt; config will continue to work if you ever decide to
bring your config to a NixOS system.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can find the full configuration after this blog post
&lt;a href=&quot;https://raw.githubusercontent.com/davish/nix-on-mac/part-2/flake.nix&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It’s a matter of preference when you use home-manager’s provided DSLs for configuring your
apps and when you’d rather just write dotfiles and sync them to the right
spot. Personally, I like the DSLs, even if they lock me more into using Nix. Feel free to
explore the &lt;a href=&quot;https://nix-community.github.io/home-manager/options.xhtml#opt-home.file&quot;&gt;home-manager configuration
options&lt;/a&gt; to see
what’s possible!&lt;/p&gt;
&lt;p&gt;In Part 3, we’ll use &lt;code&gt;home-manager&lt;/code&gt; to fully configure VSCode — declaratively managing
extensions, themes and user settings, all through Nix. See you then!&lt;/p&gt;</description><pubDate>Sat, 17 Feb 2024 17:00:00 GMT</pubDate></item><item><title>Package management on macOS with nix-darwin</title><link>https://davi.sh/blog/2024/01/nix-darwin/</link><guid isPermaLink="true">https://davi.sh/blog/2024/01/nix-darwin/</guid><description>&lt;p&gt;&lt;a href=&quot;https://davi.sh/blog/2023/12/what-i-like-about-nix/&quot;&gt;I think Nix is really cool&lt;/a&gt;. Nix the package
manager and functional configuration language is most often associated with NixOS the Linux distro,
but &lt;code&gt;nix-darwin&lt;/code&gt; makes it almost as easy to declaratively configure macOS as it is to configure
NixOS installations. Even if you’ll still relying on Homebrew for package management and never touch
nixpkgs, I’d say that Nix with &lt;code&gt;nix-darwin&lt;/code&gt; provides the best way to manage packages and system
configuration on macOS.&lt;/p&gt;
&lt;p&gt;Unfortunately, the resources for getting started and integrating different parts of the Nix
ecosystem are not particularly approachable for beginners. When I started out I would often use
GitHub’s code search to trawl through other people’s configs and try different snippets until I
found what actually worked. Inspired by &lt;a href=&quot;https://arne.me/articles/emacs-from-scratch-part-one-foundations&quot;&gt;Arne Bahlo’s Emacs from Scratch
series&lt;/a&gt;, I wanted to create a
guide to help folks get started with Nix on macOS from scratch, step by step.&lt;/p&gt;
&lt;p&gt;Throughout this series we’ll create a declarative system configuration with Nix where you can
manage anything from your shell aliases to what VSCode extensions you have installed to running
daemons with launchd. We’ll build up to this incrementally: by the end of this post, you’ll have Nix
installed on your system and be able to declaratively install system-level packages from either
Nixpkgs or Homebrew.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id=&quot;installing-nix&quot;&gt;Installing Nix&lt;/h2&gt;
&lt;p&gt;I recommend using the &lt;a href=&quot;https://determinate.systems/&quot;&gt;Determinate
Systems&lt;/a&gt; Nix installer. They have a
&lt;a href=&quot;https://github.com/DeterminateSystems/nix-installer&quot;&gt;command-line utility&lt;/a&gt; and also recently came
out with a &lt;a href=&quot;https://determinate.systems/posts/graphical-nix-installer&quot;&gt;graphical installer&lt;/a&gt; if you’d
prefer that.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-nix-darwin&quot;&gt;Setting up &lt;code&gt;nix-darwin&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/LnL7/nix-darwin&quot;&gt;&lt;code&gt;nix-darwin&lt;/code&gt;&lt;/a&gt; is a Nix library that makes it easy to configure
macOS through Nix.&lt;/p&gt;
&lt;p&gt;Nix is a programming language, and Nix configurations are programs. All programs need an
entrypoint, we’ll be using a flake&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; to provide the entrypoint to our configuration.&lt;/p&gt;
&lt;p&gt;Below is our minimal flake that calls &lt;code&gt;nix-darwin&lt;/code&gt;. Make sure to replace &lt;code&gt;$USER&lt;/code&gt; with your username
and &lt;code&gt;$HOSTNAME&lt;/code&gt; with your system’s hostname.&lt;/p&gt;
&lt;p&gt;You can place this flake in any directory you’d like. For the purposes of this series, we’ll assume
that the flake lives at &lt;code&gt;~/.config/nix/flake.nix&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ~/.config/nix/flake.nix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  description&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;My system configuration&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  inputs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;github:NixOS/nixpkgs/nixpkgs-unstable&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    nix-darwin&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;github:LnL7/nix-darwin&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        inputs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;follows&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;nixpkgs&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  outputs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; inputs@{ self&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; nix-darwin&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; nixpkgs }:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  let&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    configuration&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ... &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        services&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nix-daemon&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # Necessary for using flakes on this system.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        nix&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;settings&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;experimental-features&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;nix-command flakes&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        system&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;configurationRevision&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;rev&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; or&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;dirtyRev&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; or&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # Used for backwards compatibility. please read the changelog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # before changing: `darwin-rebuild changelog`.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        system&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stateVersion&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 4&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # The platform the configuration will be used on.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # If you&apos;re on an Intel system, replace with &quot;x86_64-darwin&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;hostPlatform&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;aarch64-darwin&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # Declare the user that will be running `nix-darwin`.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        users&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;users&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;USER&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;$USER&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            home&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;/Users/$USER&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # Create /etc/zshrc that loads the nix-darwin environment.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        programs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;zsh&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        environment&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;systemPackages&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [ ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    darwinConfigurations&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;$HOSTNAME&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; nix-darwin&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;lib&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;darwinSystem&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      modules&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;         configuration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;activating-our-nix-darwin-config&quot;&gt;Activating our &lt;code&gt;nix-darwin&lt;/code&gt; config&lt;/h2&gt;
&lt;p&gt;One of the stranger footguns when using Nix flakes is that all files referenced by a flake must be
checked into source control. This means that you’ll need to have &lt;code&gt;git&lt;/code&gt; installed before we set up
our Nix environment&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Files just need to be staged to be accessible, not committed, so &lt;code&gt;git add&lt;/code&gt;
is sufficient until you want to back up your config to a remote repository.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ~/.config/nix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; init&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; flake.nix&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once all this is set up, we can run &lt;code&gt;nix-darwin&lt;/code&gt; to activate our configuration:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nix&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nix-darwin&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --extra-experimental-features&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nix-command&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --extra-experimental-features&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; flakes&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; switch&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --flake&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ~/.config/nix&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;nix-darwin&lt;/code&gt; requires &lt;code&gt;sudo&lt;/code&gt;, so you’ll be prompted for your password. Nix may error out if there
are files that already exist at paths that it’s trying to replace. Feel free to either &lt;code&gt;rm&lt;/code&gt; these or
&lt;code&gt;mv&lt;/code&gt; them to a backup location, and then re-run the line above.&lt;/p&gt;
&lt;p&gt;Once the command succeeds, open up a new terminal window to pick up the new zsh environment and
confirm that &lt;code&gt;darwin-rebuild&lt;/code&gt; is installed on your path:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; darwin-rebuild&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --help&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;darwin-rebuild&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [--help] {&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;edit&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; switch&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; activate&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; build&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; check&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; changelog}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;               [--list-generations] [{--profile-name &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;} name] [--rollback]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;               [{--switch-generation &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; -G&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;} generation] [--verbose...] [-v...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;               [-Q] [{--max-jobs &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; -j} number] [--cores number] [--dry-run]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;               [--keep-going] [-k] [--keep-failed] [-K] [--fallback] [--show-trace]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;               [-I path] [--option name value] [--arg name value] [--argstr name value]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;               [--flake flake] [--update-input input flake] [--impure] [--recreate-lock-file]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;               [--no-update-lock-file] [--refresh] &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Congrats on setting up &lt;code&gt;nix-darwin&lt;/code&gt;! Our configuration is active, but it doesn’t do anything useful
yet. Let’s change that.&lt;/p&gt;
&lt;h2 id=&quot;installing-your-first-nix-package&quot;&gt;Installing your first Nix package&lt;/h2&gt;
&lt;p&gt;It’s time to install our first package from the nixpkgs repository. Update the list of
&lt;code&gt;systemPackages&lt;/code&gt; declared in &lt;code&gt;flake.nix&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;environment&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;systemPackages&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;neofetch&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;vim&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we’re setting the attribute &lt;code&gt;environment.systemPackages&lt;/code&gt; to a
&lt;a href=&quot;https://nixos.org/manual/nix/stable/language/values#list&quot;&gt;list&lt;/a&gt;. It’s important to point out that
&lt;strong&gt;lists in Nix are space-separated&lt;/strong&gt; rather than comma-separated like most other languages.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pkgs&lt;/code&gt; refers to &lt;code&gt;nixpkgs&lt;/code&gt;, the standard repository for finding packages to be installed with
Nix&lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. Both &lt;code&gt;neofetch&lt;/code&gt; and &lt;code&gt;vim&lt;/code&gt; are &lt;strong&gt;&lt;em&gt;derivations&lt;/em&gt;&lt;/strong&gt; within nixpkgs.&lt;/p&gt;
&lt;p&gt;To rebuild our Nix config, we don’t have to use the super long &lt;code&gt;nix run&lt;/code&gt; command from above anymore
since &lt;code&gt;nix-darwin&lt;/code&gt; added the &lt;code&gt;darwin-rebuild&lt;/code&gt; command to our &lt;code&gt;PATH&lt;/code&gt;. From now on, we just need
to run:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; darwin-rebuild&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; switch&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --flake&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ~/.config/nix&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once this runs successfully, we now have a new command in our &lt;code&gt;PATH&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; neofetch&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -L&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;                    c.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;                 ,xNMM.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;               .OMMMMo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;               lMM&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;     .;loddo:.  .olloddol;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;   cKMMMMMMMMMMNWMMMMMMMMMM0:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; .KMMMMMMMMMMMMMMMMMMMMMMMWd.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; XMMMMMMMMMMMMMMMMMMMMMMMX.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;;MMMMMMMMMMMMMMMMMMMMMMMM:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;:MMMMMMMMMMMMMMMMMMMMMMMM:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.MMMMMMMMMMMMMMMMMMMMMMMMX.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; kMMMMMMMMMMMMMMMMMMMMMMMMWd.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;XMMMMMMMMMMMMMMMMMMMMMMMMMMk&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  &apos;XMMMMMMMMMMMMMMMMMMMMMMMMK.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    kMMMMMMMMMMMMMMMMMMMMMMd&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;     ;KMMMMMMMWXXWMMMMMMMk.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;       &quot;cooc*&quot;    &quot;*coo&apos;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we’re really cooking!&lt;/p&gt;
&lt;h2 id=&quot;searching-nixpkgs&quot;&gt;Searching Nixpkgs&lt;/h2&gt;
&lt;p&gt;Check out &lt;a href=&quot;https://search.nixos.org/packages&quot;&gt;nixpkgs search&lt;/a&gt; to find other packages you might want
to install.&lt;/p&gt;
&lt;h2 id=&quot;installing-packages-from-homebrew&quot;&gt;Installing packages from Homebrew&lt;/h2&gt;
&lt;p&gt;Nixpkgs is expansive, but some programs are still only available from
&lt;a href=&quot;https://brew.sh/&quot;&gt;Homebrew&lt;/a&gt;. &lt;code&gt;nix-darwin&lt;/code&gt; provides what I think is the best interface for Homebrew
formulae, casks, and even Mac App Store apps&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. Let’s add this right under
&lt;code&gt;environment.systemPackages&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;homebrew&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # onActivation.cleanup = &quot;uninstall&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    taps&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    brews&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;cowsay&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    casks&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running &lt;code&gt;darwin-rebuild switch --flake ~/.config/nix&lt;/code&gt; again will install the Homebrew formula
specified in the &lt;code&gt;brews&lt;/code&gt; list. Try it out:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cowsay&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;homebrew and nix can be best friends&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ______________________________________&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; homebrew and nix can be best friends &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; --------------------------------------&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        \&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;   ^__^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;         \&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  (oo)&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\_&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;______&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            (&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;__&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;       )&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\/\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                ||&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;----w&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                ||&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;     ||&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you’re on a Mac where you’ve been using Homebrew for a while, you can run &lt;code&gt;brew list&lt;/code&gt; and &lt;code&gt;brew list --cask&lt;/code&gt; to list your installed formulae and casks. Once you’ve added every package you want to carry
over to the corresponding lists in your Nix config, uncomment &lt;code&gt;onActivation.cleanup = &quot;uninstall&quot;&lt;/code&gt;. Your homebrew config in &lt;code&gt;nix-darwin&lt;/code&gt; is now &lt;em&gt;declarative&lt;/em&gt;: only the packages specified
in your &lt;code&gt;flake.nix&lt;/code&gt; will be installed, and if you ever remove a package from the lists here it will
be uninstalled the next time you reload with &lt;code&gt;darwin-rebuild switch&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;nixpkgs-vs-homebrew&quot;&gt;Nixpkgs vs Homebrew&lt;/h2&gt;
&lt;p&gt;While &lt;code&gt;nix-darwin&lt;/code&gt; makes it easy to install packages with Homebrew, I’d recommend trying to find the
corresponding derivations within Nixpkgs when possible rather than relying solely on Homebrew
formulae. As the series goes on we’ll see how native Nix derivations are easier work with in Nix.&lt;/p&gt;
&lt;h2 id=&quot;additional-configuration&quot;&gt;Additional configuration&lt;/h2&gt;
&lt;p&gt;You’ve probably gotten tired of entering your password everytime you reload your config. Luckily,
there’s a one-liner to enable Touch ID for &lt;code&gt;sudo&lt;/code&gt;, which you can put at the end of your
configuration:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;let&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    configuration&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ... &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        security&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pam&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;enableSudoTouchIdAuth&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All of &lt;code&gt;nix-darwin&lt;/code&gt;’s configuration options are worth exploring — we’ll go more in-depth into some
of them in future installments of this series, but in case you’re curious, the you can explore all
the &lt;a href=&quot;https://daiderd.com/nix-darwin/manual/index.html&quot;&gt;configuration options&lt;/a&gt; and start making your
config your own!&lt;/p&gt;
&lt;p&gt;If you’d like to see the full file that we’ve built up over the course of this post, you can find it
&lt;a href=&quot;https://raw.githubusercontent.com/davish/nix-on-mac/part-1/flake.nix&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now that we have a handle on our system configuration, in the next post we’ll set up
&lt;a href=&quot;https://github.com/nix-community/home-manager&quot;&gt;&lt;code&gt;home-manager&lt;/code&gt;&lt;/a&gt; and use it manage dotfiles and other
program configuration.&lt;/p&gt;
&lt;p&gt;Until next time!&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Since this series will focus on system configuration rather than development environments,
we’ll only be creating this one flake and won’t cover flakes in-depth. If want to read more
about flakes, feel free to check out &lt;a href=&quot;https://jvns.ca/blog/2023/11/11/notes-on-nix-flakes/&quot;&gt;Julia Evans’s blog post on
flakes&lt;/a&gt; and the &lt;a href=&quot;https://zero-to-nix.com/concepts/flakes&quot;&gt;Zero to Nix wiki
page&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;If you’re on a brand new machine, the first time you run &lt;code&gt;git&lt;/code&gt; in the terminal you should be
prompted to install Xcode Command Line Tools, which includes &lt;code&gt;git&lt;/code&gt;. &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;While it’s not necessary to fully understand this right now, the &lt;code&gt;configuration&lt;/code&gt; value that
we’re defining is a &lt;a href=&quot;https://nixos.wiki/wiki/NixOS_modules&quot;&gt;Nix module&lt;/a&gt;. The
&lt;code&gt;nix-darwin.lib.darwinSystem&lt;/code&gt; function that’s called at the bottom of the file is responsible
for passing &lt;code&gt;nixpkgs&lt;/code&gt; through to &lt;code&gt;configuration&lt;/code&gt; with the name &lt;code&gt;pkgs&lt;/code&gt;. We’ll dive deeper into
Nix modules in a later post. &lt;a href=&quot;#user-content-fnref-4&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;While we won’t be installing any App Store apps in this post, you can check out the
description in the &lt;a href=&quot;https://daiderd.com/nix-darwin/manual/index.html#opt-homebrew.masApps&quot;&gt;nix-darwin
documentation&lt;/a&gt; for more
information. &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><pubDate>Mon, 29 Jan 2024 17:00:00 GMT</pubDate></item><item><title>What I Like About Nix</title><link>https://davi.sh/blog/2023/12/what-i-like-about-nix/</link><guid isPermaLink="true">https://davi.sh/blog/2023/12/what-i-like-about-nix/</guid><description>&lt;p&gt;I got a new computer recently and decided to take the plunge setting it up with Nix&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. &lt;a href=&quot;https://davi.sh/til/nix/nix-macos-setup/&quot;&gt;I wrote up a snippet&lt;/a&gt; on how I set everything up and you can find my whole configuration &lt;a href=&quot;https://github.com/davish/setup&quot;&gt;on GitHub&lt;/a&gt;. I’ve only scratched the surface of what Nix can do – In this post I wanted to focus on my subjective impression of Nix so far and why I feel I’ve been enjoying it.&lt;/p&gt;
&lt;h2 id=&quot;long-term-shared-memory&quot;&gt;Long-term, shared memory&lt;/h2&gt;
&lt;p&gt;No need to worry about remembering what switch you flipped in settings six months ago after reading an OSXDaily.com post, or what command you ran in a shell at 3am the morning you unboxed your new computer. Any change you’ve made to, for example, &lt;a href=&quot;https://github.com/davish/setup/blob/main/darwin/default.nix#L46&quot;&gt;hide the dock by default&lt;/a&gt; or allow for repeating keys in &lt;a href=&quot;https://github.com/davish/setup/blob/main/darwin/default.nix#L48-L49&quot;&gt;VSCode and other editors&lt;/a&gt; is there for later review.&lt;/p&gt;
&lt;p&gt;On macOS these system preferences are normally either configured in the point-and-click System Preferences interface or specified in loosely documented &lt;code&gt;defaults write&lt;/code&gt; invocations that you can find all over the internet. &lt;a href=&quot;https://github.com/LnL7/nix-darwin&quot;&gt;&lt;code&gt;nix-darwin&lt;/code&gt;&lt;/a&gt; is a Nix tool I’m using that lets you configure virtually all of these settings from a Nix file.&lt;/p&gt;
&lt;p&gt;Making your own configuration declarative is good enough, but on top of that people have built up modules and abstractions around more complex configuration like &lt;a href=&quot;https://github.com/LnL7/nix-darwin/blob/4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d/modules/security/pam.nix&quot;&gt;enabling &lt;code&gt;sudo&lt;/code&gt; to use TouchID&lt;/a&gt; or &lt;a href=&quot;https://github.com/LnL7/nix-darwin/tree/4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d/modules/services&quot;&gt;running daemons through launchd&lt;/a&gt; that you can enable with a single line of code.&lt;/p&gt;
&lt;h2 id=&quot;a-complete-picture-of-your-environment&quot;&gt;A complete picture of your environment&lt;/h2&gt;
&lt;p&gt;All of my dotfiles, editor themes and extensions, and command-line programs are defined in a single repository. It makes it a lot easier to see how it all interacts with each other and hopefully avoid situations where different dependencies conflict. No one wants to end up here, but it happens all too often, at least to me:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/python_environment.png&quot; alt=&quot;xkcd comic describing a chaotic python environment&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;synchronization-between-machines&quot;&gt;Synchronization between machines&lt;/h2&gt;
&lt;p&gt;I have a desktop and a laptop. They’re now running virtually the same Nix configuration, out of the same repository, with a few tweaks in host-specific overrides. I can work out the kinks in getting Emacs to play nicely with the &lt;a href=&quot;https://github.com/koekeishiya/yabai&quot;&gt;Yabai window manager&lt;/a&gt; on my desktop and have it all work on my laptop in a matter of minutes once I pull down the updated configuration repository and reload my Nix environment.&lt;/p&gt;
&lt;p&gt;I should mention these two machines are running different versions of macOS, and virtually everything has worked flawlessly across both.&lt;/p&gt;
&lt;h2 id=&quot;reducing-manual-toil&quot;&gt;Reducing manual toil&lt;/h2&gt;
&lt;p&gt;I’ve used &lt;a href=&quot;https://karabiner-elements.pqrs.org/&quot;&gt;Karabiner Elements&lt;/a&gt; for years to remap some keys on my keyboard. More recently I’ve moved my configuration over to &lt;a href=&quot;https://github.com/yqrashawn/GokuRakuJoudo&quot;&gt;Goku&lt;/a&gt;, a DSL for Karabiner on top of the JSON configuration format. Every time I wanted to change my configuration I would need to remember to run the &lt;code&gt;goku&lt;/code&gt; command. Now that command is run automatically by &lt;a href=&quot;https://github.com/nix-community/home-manager&quot;&gt;&lt;code&gt;home-manager&lt;/code&gt;&lt;/a&gt; whenever my &lt;code&gt;karabiner.edn&lt;/code&gt; file changes. When I change my &lt;code&gt;skhd&lt;/code&gt; or &lt;code&gt;yabai&lt;/code&gt; configuration, Nix will automatically restart those services so they pick up their new settings.&lt;/p&gt;
&lt;p&gt;Standardizing the refreshing of all configuration to a single command (&lt;code&gt;darwin-rebuild switch&lt;/code&gt;) and running everything only when it’s needed has been a small change that’s significantly reduced the cognitive load of having a lot of subsystems that may require new configuration, occasional restarts, or other babysitting from time-to-time.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If the entirety of Nix was a DSL for a package manager and system configuration system I’d be satisfied. But there’s so much more to Nix, from development environments to production artifacts. I’m excited to dive more into those corners of the ecosystem going forward, and hopefully write about it too both here on the blog and over on &lt;a href=&quot;https://davi.sh/til/&quot;&gt;the TIL page&lt;/a&gt;!&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Nix isn’t the easiest thing to introduce since it can refer to a few different distinct components or the whole ecosystem. In case you haven’t heard of Nix before or just want to learn more, I find &lt;a href=&quot;https://zero-to-nix.com/concepts/nix&quot;&gt;the conceptual documentation on Zero to Nix&lt;/a&gt; to be more approachable than the official docs. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><pubDate>Tue, 12 Dec 2023 12:00:00 GMT</pubDate></item><item><title>[TIL] nix develop -c $SHELL: Run zsh with &apos;nix develop&apos;</title><link>https://davi.sh/til/nix/nix-develop-c/</link><guid isPermaLink="true">https://davi.sh/til/nix/nix-develop-c/</guid><description>&lt;p&gt;I built my &lt;a href=&quot;https://github.com/davish/website-astro/blob/main/flake.nix&quot;&gt;development flake for this website&lt;/a&gt; with the &lt;a href=&quot;https://github.com/DeterminateSystems/fh&quot;&gt;FlakeHub CLI&lt;/a&gt;. That flake uses &lt;a href=&quot;https://ryantm.github.io/nixpkgs/builders/special/mkshell/&quot;&gt;&lt;code&gt;pkgs.mkShell&lt;/code&gt;&lt;/a&gt; to load in binary dependencies to the development environment. I run zsh defined and configured by &lt;code&gt;nix-darwin&lt;/code&gt;, so it was pretty jarring getting thrown into a plain bash shell when running &lt;code&gt;nix develop&lt;/code&gt;. Luckily the fix is simple. Rather than just running &lt;code&gt;nix develop&lt;/code&gt;, run:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nix&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; develop&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $SHELL&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and your normal shell will be run instead of the default bash shell.&lt;/p&gt;
&lt;p&gt;(via &lt;a href=&quot;https://discourse.nixos.org/t/using-nix-develop-opens-bash-instead-of-zsh/25075&quot;&gt;this discussion&lt;/a&gt;)&lt;/p&gt;</description><pubDate>Sat, 18 Nov 2023 17:00:00 GMT</pubDate></item><item><title>[TIL] nix-darwin and home-manager: Setting up Nix on macOS</title><link>https://davi.sh/til/nix/nix-macos-setup/</link><guid isPermaLink="true">https://davi.sh/til/nix/nix-macos-setup/</guid><description>&lt;p&gt;I recently got a new computer and am spending some time actually digging into Nix for managing dependencies. My main issue the last time I tried using Nix was how tough it was for me to wade through the documentation, so I was determined to try a different path this time. I started off with the very opinionated &lt;a href=&quot;https://zero-to-nix.com/start/install&quot;&gt;Zero to Nix&lt;/a&gt; tutorial series, which I thought was a great introduction.&lt;/p&gt;
&lt;p&gt;After learning about Nix and Flakes, and getting my website building locally with a nix-powered development environment courtesy of the guidance in &lt;a href=&quot;https://zero-to-nix.com/start/init-flake&quot;&gt;Part 6&lt;/a&gt;, the next step was to see what else I could do. I’d heard of two different tools, &lt;a href=&quot;https://github.com/nix-community/home-manager&quot;&gt;&lt;code&gt;home-manager&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/LnL7/nix-darwin&quot;&gt;&lt;code&gt;nix-darwin&lt;/code&gt;&lt;/a&gt;. I started off setting up &lt;code&gt;home-manager&lt;/code&gt;, but I was confused on how I should be installing graphical applications like VS Code, and system utilities like Karabiner Elements. It turned out &lt;code&gt;nix-darwin&lt;/code&gt; was the answer there.&lt;/p&gt;
&lt;p&gt;To paraphrase a &lt;a href=&quot;https://www.reddit.com/r/NixOS/comments/jznwne/effectively_combining_homemanager_and_nixdarwin/&quot;&gt;Reddit thread I found&lt;/a&gt;, &lt;code&gt;nix-darwin&lt;/code&gt; manages system-level programs and configuration, while &lt;code&gt;home-manager&lt;/code&gt; manages user configuration. You can actually get &lt;code&gt;nix-darwin&lt;/code&gt; to call into &lt;code&gt;home-manager&lt;/code&gt; as part of its own configuration to use both at the same time. This is described in the &lt;a href=&quot;https://nix-community.github.io/home-manager/index.html#sec-install-nix-darwin-module&quot;&gt;&lt;code&gt;home-manager&lt;/code&gt; reference docs&lt;/a&gt;, but like many Nix resources, it’s not very simple for a beginner to figure out how to actually put this snippet to use in their own configuration.&lt;/p&gt;
&lt;p&gt;Before following these instructions, you should follow the &lt;a href=&quot;https://zero-to-nix.com/start/install&quot;&gt;Zero to Nix&lt;/a&gt; guide or install Nix on your system in some other way.&lt;/p&gt;
&lt;p&gt;I’m going to lay out the basic steps that I followed below while sharing each configuration file.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a nix-darwin flake file. From the &lt;a href=&quot;https://github.com/LnL7/nix-darwin/blob/b658dbd85a1c70a15759b470d7b88c0c95f497be/README.md#step-1-creating-flakenix&quot;&gt;README&lt;/a&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ~/.config/nix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ~/.config/nix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nix&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; flake&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; init&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nix-darwin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;sed&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;&apos;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;s/simple/$(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;scutil&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --get&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; LocalHostName)/&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; flake.nix&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The default flake puts the configuration inline to the flake.nix. I decided to copy/paste that configuration out into a separate file called &lt;code&gt;darwin.nix&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# darwin.nix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{ pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ... &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # List packages installed in system profile. To search by name, run:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # $ nix-env -qaP | grep wget&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    environment&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;systemPackages&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    [ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;vim&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # Auto upgrade nix package and the daemon service.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    services&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nix-daemon&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    services&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;karabiner-elements&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # nix.package = pkgs.nix;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # Necessary for using flakes on this system.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    nix&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;settings&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;experimental-features&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;nix-command flakes&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # Create /etc/zshrc that loads the nix-darwin environment.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    programs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;zsh&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# default shell on catalina&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # programs.fish.enable = true;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # Used for backwards compatibility, please read the changelog before changing.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # $ darwin-rebuild changelog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    system&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stateVersion&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 4&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # The platform the configuration will be used on.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;hostPlatform&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;aarch64-darwin&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    users&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;users&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;davish&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;davish&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        home&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;/Users/davish&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should remember to change your username and home directory to match.&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Modify &lt;code&gt;flake.nix&lt;/code&gt; to work with &lt;code&gt;home-manager&lt;/code&gt;.
The example code can be found &lt;a href=&quot;https://nix-community.github.io/home-manager/index.html#sec-flakes-nix-darwin-module&quot;&gt;in the &lt;code&gt;home-manager&lt;/code&gt; docs&lt;/a&gt;. In case that link breaks, here it is:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# flake.nix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  description&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Darwin configuration&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  inputs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;github:nixos/nixpkgs/nixos-unstable&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    darwin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;github:lnl7/nix-darwin&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    darwin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;inputs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;follows&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;nixpkgs&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;github:nix-community/home-manager&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;inputs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;follows&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;nixpkgs&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  outputs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; inputs@{ nixpkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; home-manager&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; darwin&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ... &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    darwinConfigurations&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      hostname&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; darwin&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;lib&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;darwinSystem&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        system&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;aarch64-darwin&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        modules&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          ./configuration.nix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;          home-manager&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;darwinModules&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;home-manager&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useGlobalPkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useUserPackages&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;            home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;users&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;jdoe&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; import&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ./home.nix&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # Optionally, use home-manager.extraSpecialArgs to pass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # arguments to home.nix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Create a &lt;code&gt;home.nix&lt;/code&gt; file. Here is the one that was generated when I tried to set up home-manager on its own. I’ll replicate it here because the comments were useful as a tutorial on its own:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;nix&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# home.nix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{ config&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; pkgs&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ... &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Home Manager needs a bit of information about you and the paths it should&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # manage.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # This value determines the Home Manager release that your configuration is&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # compatible with. This helps avoid breakage when a new Home Manager release&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # introduces backwards incompatible changes.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  #&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # You should not change this value, even if you update Home Manager. If you do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # want to update the value, then make sure to first check the Home Manager&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # release notes.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  home&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stateVersion&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;23.05&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Please read the comment before changing.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # The home.packages option allows you to install Nix packages into your&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # environment.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  home&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;packages&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # Adds the &apos;hello&apos; command to your environment. It prints a friendly&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # &quot;Hello, world!&quot; when run.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # pkgs.hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # It is sometimes useful to fine-tune packages, for example, by applying&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # overrides. You can do that directly here, just don&apos;t forget the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # parentheses. Maybe you want to install Nerd Fonts with a limited number of&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # fonts?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # (pkgs.nerdfonts.override { fonts = [ &quot;FantasqueSansMono&quot; ]; })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # You can also create simple shell scripts directly inside your&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # configuration. For example, this adds a command &apos;my-hello&apos; to your&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # environment:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # (pkgs.writeShellScriptBin &quot;my-hello&quot; &apos;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    #   echo &quot;Hello, ${config.home.username}!&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # &apos;&apos;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Home Manager is pretty good at managing dotfiles. The primary way to manage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # plain files is through &apos;home.file&apos;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  home&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # Building this configuration will create a copy of &apos;dotfiles/screenrc&apos; in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # the Nix store. Activating the configuration will then make &apos;~/.screenrc&apos; a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # symlink to the Nix store copy.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # &quot;.screenrc&quot;.source = dotfiles/screenrc;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # # You can also set the file content immediately.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # &quot;.gradle/gradle.properties&quot;.text = &apos;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    #   org.gradle.console=verbose&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    #   org.gradle.daemon.idletimeout=3600000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # &apos;&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # You can also manage environment variables but you will have to manually&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  #&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  #  ~/.nix-profile/etc/profile.d/hm-session-vars.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  #&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # or&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  #&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  #  /etc/profiles/per-user/davish/etc/profile.d/hm-session-vars.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  #&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # if you don&apos;t want to manage your shell through Home Manager.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  home&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;sessionVariables&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # EDITOR = &quot;emacs&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Let Home Manager install and manage itself.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  programs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;home-manager&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;enable&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And there you have it! You can install nix-darwin with:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nix&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nix-darwin&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --experimental-feature&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nix-command&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --experimental-feature&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; flakes&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; switch&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --flake&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ~/.config/nix&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now run &lt;code&gt;darwin-rebuild switch --flake ~/.config/nix&lt;/code&gt; to rebuild all your configuration whenever you make a change. Another useful link is the list of &lt;a href=&quot;https://daiderd.com/nix-darwin/manual/index.html&quot;&gt;nix-darwin configuration options&lt;/a&gt;. I believe this is the best place to go for information on what can be configured with &lt;code&gt;nix-darwin&lt;/code&gt;.&lt;/p&gt;</description><pubDate>Fri, 17 Nov 2023 17:00:00 GMT</pubDate></item><item><title>Honing Your Craft</title><link>https://davi.sh/blog/2023/05/craft/</link><guid isPermaLink="true">https://davi.sh/blog/2023/05/craft/</guid><description>&lt;p&gt;Pablo Picasso was one of the most famous modern artists of the 20th century. While many of his paintings border on the abstract, he sharpened his artisitc skill on more realistic and traditional styles. Novelty and creativity are important parts of great art, but just as necessary to making anything great is the technical ability to take an idea and represent it in the real world — even abstract expressionists could paint unbelieveably realistic landscapes if they felt the need.&lt;/p&gt;
&lt;p&gt;I’m certainly no artist, but the lesson here applies more broadly: practicing technical skills on their own is an important part of the creative process.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;&lt;img src=&quot;/blog/images/picasso_science-and-charity.jpg&quot; alt=&quot;Science and Charity by Picasso. The caption follows in the next line of text.&quot;&gt; &lt;em&gt;&lt;em&gt;Science and Charity&lt;/em&gt; by Pablo Picasso, 1897 (age 16)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/images/picasso_guernica.jpg&quot; alt=&quot;Guernica by Pablo Picasso. The caption follows in the next line of text.&quot;&gt; &lt;em&gt;&lt;em&gt;Guernica&lt;/em&gt; by Pablo Picasso, 1937 (age 56).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I’ve got more than a few ideas for open-source projects and apps that I’d like to implement. I also have more than a few languages, frameworks and technologies that I’m itching to learn how to use. For a long time I’ve taken to heart the advice that the best way to learn a new programming language or framework is to build a larger-scale project with it that you’re excited about. What I’m realizing, though, is that &lt;a href=&quot;https://mcfunley.com/choose-boring-technology&quot;&gt;innovation tokens&lt;/a&gt; apply just as much to side projects where you’re looking to grow and learn as they do for production applications. The more mental energy spent designing a UI and triaging bug reports, the less energy there is for diving deep into a new language, investigating performance bottlenecks and learning other tools.&lt;/p&gt;
&lt;p&gt;My thoughts on this really solidified as I started a read-through of &lt;a href=&quot;http://craftinginterpreters.com/&quot;&gt;Crafting Interpreters&lt;/a&gt; with Kotlin. Even though the book fully spells out the code and structure of the interpreter, just translating the code to a different language has helped me learn a ton about language implementation in general and Kotlin in particular along the way.&lt;/p&gt;
&lt;p&gt;This isn’t to say that you shouldn’t push forward on your own ideas. But in order to put the best work out there, it’s important to also dedicate energy to honing technical skill on its own with some more well-defined projects that still require a lot of depth of implementation. &lt;a href=&quot;https://austinhenley.com/blog/challengingprojects.html&quot;&gt;Challenging Projects Every Programmer Should Try&lt;/a&gt; and &lt;a href=&quot;https://austinhenley.com/blog/morechallengingprojects.html&quot;&gt;its sequel&lt;/a&gt; are good lists to start off with.&lt;/p&gt;</description><pubDate>Sun, 21 May 2023 09:53:48 GMT</pubDate></item><item><title>[TIL] freeport: Kill a process that&apos;s hogging a port on MacOS</title><link>https://davi.sh/til/bash/freeport/</link><guid isPermaLink="true">https://davi.sh/til/bash/freeport/</guid><description>&lt;p&gt;Every once in a while, a development server on my laptop gets stuck open when the terminal that spawned it has already closed. I normally have to take 5 minutes or so to search through StackOverflow to find out how to kill the process that’s hogging up that port, so I finally made a script for it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#!/bin/sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;$#&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; -eq&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]; &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;usage: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;$0&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &amp;#x3C;port number&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    exit&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;lsof&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; tcp:&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;$1&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; xargs&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; kill&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -9&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also just copy/paste the last line and replace &lt;code&gt;$1&lt;/code&gt; with the port number in question, if bash scripts aren’t your thing.&lt;/p&gt;</description><pubDate>Sat, 22 Apr 2023 09:54:46 GMT</pubDate></item><item><title>[TIL] Json: Typescript type for the JSON spec</title><link>https://davi.sh/til/typescript/json-type/</link><guid isPermaLink="true">https://davi.sh/til/typescript/json-type/</guid><description>&lt;p&gt;Some JavaScript types &lt;a href=&quot;https://stackoverflow.com/questions/11491938/issues-with-date-when-using-json-stringify-and-json-parse&quot;&gt;like dates&lt;/a&gt; are not representable directly with JSON and require extra care to parse back out to a JavaScript object after they’re serialized. If you want to make sure that you are only stringify-ing JSON-safe types, you can use this type to represent objects compatible with the &lt;a href=&quot;https://www.json.org&quot;&gt;JSON spec&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Json&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    |&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { [&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;[]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    |&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    |&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; number&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    |&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    |&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    |&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; safeStringify&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; JSON&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(obj);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have some type that is &lt;em&gt;not&lt;/em&gt; JSON-compatible that you want to serialize, this type will help you write a &lt;code&gt;serialize()&lt;/code&gt; function where the Typescript compiler confirms that the function is producing a safe-to-stringify value:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; MyType&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    date&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Date&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    title&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,	&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; serialize&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; MyType&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {title: obj.title, date: obj.date.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;toISOString&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</description><pubDate>Sun, 09 Apr 2023 09:45:16 GMT</pubDate></item><item><title>[TIL] typesEqual: Ask the Typescript compiler how two types are different</title><link>https://davi.sh/til/typescript/types-equal/</link><guid isPermaLink="true">https://davi.sh/til/typescript/types-equal/</guid><description>&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; FirstType&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; SecondType&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; typesEqual&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;A&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;B&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; A&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;C&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; B&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// This will fail to compile if FirstType and SecondType are not equivalent.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;typesEqual&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;FirstType&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;SecondType&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;FirstType&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Found in &lt;a href=&quot;https://stackoverflow.com/a/69413184/21359118&quot;&gt;this StackOverflow answer&lt;/a&gt; by &lt;a href=&quot;https://stackoverflow.com/users/5103610/almaju&quot;&gt;Almaju&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was replacing a hand-written validator with &lt;a href=&quot;https://zod.dev&quot;&gt;Zod&lt;/a&gt; and wanted to make sure my new type inferred with Zod was equivalent to my old hand-rolled type. The assertion on the last line of this snippet will fail to compile if the two types passed in are not equal. The error will explain exactly which properties are missing and/or incompatible, so you can adjust your new type as needed to match.&lt;/p&gt;</description><pubDate>Thu, 06 Apr 2023 17:00:00 GMT</pubDate></item><item><title>Asking the Right Questions to ChatGPT</title><link>https://davi.sh/blog/2023/03/gpt-right-questions/</link><guid isPermaLink="true">https://davi.sh/blog/2023/03/gpt-right-questions/</guid><description>&lt;p&gt;&lt;em&gt;This is my generative AI take. I’m sure there are many like it, but this one is mine&lt;a href=&quot;https://www.usmcu.edu/Research/Marine-Corps-History-Division/Frequently-Requested-Topics/Marines-Rifle-Creed/&quot;&gt;.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When it comes to social trends I’m often on the far side of &lt;a href=&quot;https://en.wikipedia.org/wiki/Crossing_the_Chasm&quot;&gt;Moore’s Chasm&lt;/a&gt;. I signed up for Snapchat a year after all of my friends, and joined Instagram two years after everyone else. So it was only appropriate that it took weeks after the internet was set on fire for me to see the value that applications of Large Language Models like &lt;a href=&quot;https://openai.com/blog/chatgpt&quot;&gt;ChatGPT&lt;/a&gt; and &lt;a href=&quot;https://github.com/features/copilot&quot;&gt;GitHub Copilot&lt;/a&gt; bring to programming. But now that I’ve given it a real shot, it’s clear to me that if you have enough knowledge to ask specific tactical questions about a well-defined technology, framework or library, ChatGPT can be a huge force multiplier.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;When ChatGPT dropped a few months ago, I asked it a few questions right away, like “can you compare OCaml and Haskell?”, and “What is COVID-19?”. I wasn’t super impressed with the depth of the answers, and hallucination was pretty obvious, even if the grammar and vocabulary was suprisingly coherent and humanlike.&lt;/p&gt;
&lt;p&gt;This week I decided to give ChatGPT another shot. I’ve started working on an API built with &lt;a href=&quot;https://www.django-rest-framework.org&quot;&gt;Django REST Framework&lt;/a&gt; this week for the first time in about two years. I still had a notion of what I wanted to do even though my working knowledge of how to build stuff with the framework was rusty. While the Django and DRF documentation are both thorough, it’s often frustrating to search through them for specific examples when you know what you’re looking for.&lt;/p&gt;
&lt;p&gt;ChatGPT was able to provide me with surprisingly good answers to my intermediate-to-advanced Django questions and prompts, like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write a model definition with a &lt;code&gt;created_at&lt;/code&gt; field that is set to the current time when the model is created.&lt;/li&gt;
&lt;li&gt;I have two django models, X and Y. Y has a ForeignKey relationship to X. When creating an X in the django admin I want to be able to create Ys in-line. Is this possible? (Yes, with an example &lt;code&gt;ModelAdmin&lt;/code&gt; implementation)&lt;/li&gt;
&lt;li&gt;When should I use a through model in Django?&lt;/li&gt;
&lt;li&gt;I have a django model Y with a decimal field A and foreign keys to a User model and a model X. Is it possible to get the sum of A for all Ys in an X and the sum for all Ys linked to a User in an X in one query?&lt;/li&gt;
&lt;li&gt;I want to create a page where users can create a new X with a any number of Ys inline. How can I do this with django generic class-based views?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When searching for an answer to a technical question on Google it’s often best to try and abstract your goal into a general question to see if there’s any answers out there on the internet even tangentially related. With ChatGPT, I got the best answers when I gave it specifics about my use case and the models I was using. It was able to plug in my model names and properties that I gave it without any problem. It was also a lot easier to phrase my questions naturally without having to generalize them out like I would with Google.&lt;/p&gt;
&lt;p&gt;I see two main components that led ChatGPT to being a great copilot on this Django project. Django and DRF are old, stable, and popular. I’m sure that tons of blog posts, StackOverflow answers, and books on the framework made it in to GPT-3’s training corpus. Just as importantly, my own knowledge enabled me to ask precise questions that were within the bounds of possibility for the framework. I had worked with Django for years and knew the general vocabulary of the framework and what it was capable of. I stayed away from asking for ChatGPT’s opinion or asking high-level design or architecture questions, and I suspect those kinds of questions are where the model is more likely to hallucinate.&lt;/p&gt;
&lt;p&gt;For now, at least, LLMs are most useful when the prompter has sufficient domain knowledge to form precise questions. I don’t think the robots are here for &lt;em&gt;all&lt;/em&gt; our jobs just yet. But this is a fast-moving field: we’ll see what happens in the next 5 years – or even 5 months.&lt;/p&gt;</description><pubDate>Thu, 30 Mar 2023 14:26:43 GMT</pubDate></item><item><title>[TIL] ListSerializerMixin: Specify a different viewset serializer for lists</title><link>https://davi.sh/til/django/list-serializer-mixin/</link><guid isPermaLink="true">https://davi.sh/til/django/list-serializer-mixin/</guid><description>&lt;p&gt;I often want to include &lt;a href=&quot;https://www.django-rest-framework.org/api-guide/relations/#nested-relationships&quot;&gt;related nested objects&lt;/a&gt; in my &lt;a href=&quot;https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset&quot;&gt;Django REST Framework &lt;code&gt;ModelViewSet&lt;/code&gt;s&lt;/a&gt; for CRUD operations, but don’t want those related objects cluttering up the list view. To accomplish this, you can return a serializer from &lt;code&gt;get_serializer_class()&lt;/code&gt; that doesn’t include the nested relation when the ViewSet action is &lt;code&gt;list&lt;/code&gt;. I ended up doing this on a bunch of viewsets, so I factored the logic out into a separate mixin:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; typing &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Union&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; rest_framework &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; viewsets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ListSerializerMixin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;):  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    list_serializer_class &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; None&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; get_serializer_class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        self: Union[viewsets.GenericViewSet, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;ListSerializerMixin&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ):  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.list_serializer_class &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.action &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;list&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.list_serializer_class  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; super&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(viewsets.GenericViewSet, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).get_serializer_class()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On your viewset, you can inherit from &lt;code&gt;ListSerializerMixin&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; then add the &lt;code&gt;list_serializer_class&lt;/code&gt; attribute next to &lt;code&gt;serializer_class&lt;/code&gt; instead of overriding the method directly:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ObjectViewSet&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ListSerializerMixin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;viewsets&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ModelViewSet&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;):  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    serializer_class &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ObjectDetailSerializer  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    list_serializer_class &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ObjectListSerializer  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    permission_classes &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [IsAuthenticated]  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; get_queryset&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(self):  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Object.objects.all().prefetch_related(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;related_items&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This leads to serializers that are easier to read at-a-glance, and slightly easier to write.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Make sure to add the mixin &lt;em&gt;before&lt;/em&gt; the ViewSet in your superclass list. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><pubDate>Tue, 28 Mar 2023 17:00:00 GMT</pubDate></item><item><title>[TIL] getUrl(): URL routing for content collections</title><link>https://davi.sh/til/astro/content-url-routing/</link><guid isPermaLink="true">https://davi.sh/til/astro/content-url-routing/</guid><description>&lt;p&gt;Astro added &lt;a href=&quot;https://docs.astro.build/en/guides/content-collections/&quot;&gt;content collections&lt;/a&gt; in v2.0, and while it’s overall a big improvement for pages like blog posts where there’s a uniform schema, it makes generating links to your content just a bit harder than file-based routing. I added a helper function and some Typescript types to &lt;code&gt;content.ts&lt;/code&gt; to make it easy to map content collections to URLs in a typesafe way that your editor can help auto-complete.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; collections&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Your collections here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // see https://docs.astro.build/en/guides/content-collections/#defining-multiple-collections&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  coll1: &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;defineCollection&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  coll2: &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;defineCollection&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  coll3: &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;defineCollection&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; UrlMap&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Partial&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  [&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; keyof&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; typeof&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; collections]&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; urls&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; UrlMap&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  coll1: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/path/to-coll1/&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  coll2: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/path-to/coll2/&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  coll3: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/path/to/coll3/&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; getUrl&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;collName&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; keyof&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; typeof&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; urls, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;slug&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  urls[collName] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; slug;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In your Astro components, you can then call &lt;code&gt;getUrl(&quot;collName&quot;, slug)&lt;/code&gt; to get the URL.&lt;/p&gt;
&lt;p&gt;While it’s just a bit of string concatenation, the added type safety can make a big difference in a larger project. Even on this website, I’ve found the autocomplete for the collection name to be very helpful.&lt;/p&gt;</description><pubDate>Sun, 26 Mar 2023 17:00:00 GMT</pubDate></item><item><title>[TIL] form_valid() overload: Handle uploads with class-based generic views</title><link>https://davi.sh/til/django/upload-with-cbv/</link><guid isPermaLink="true">https://davi.sh/til/django/upload-with-cbv/</guid><description>&lt;p&gt;The Django documentation describes how to bind uploaded files when using function-based views. There’s no easy equivalent I could find on how to do the same for generic, class based views like &lt;a href=&quot;https://docs.djangoproject.com/en/4.1/ref/class-based-views/generic-editing/#django.views.generic.edit.CreateView&quot;&gt;CreateView&lt;/a&gt;. The right way to do it is to override the &lt;code&gt;form_valid()&lt;/code&gt; method on the view:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; form_valid&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(self, form):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    file&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.request.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;FILES&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.object &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; form.save(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;commit&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;False&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.object.file &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt; file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.object.save()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; HttpResponseRedirect(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.get_success_url())&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</description><pubDate>Sat, 25 Mar 2023 17:00:00 GMT</pubDate></item><item><title>Configuring VSCode as a Keyboard-Centric IDE</title><link>https://davi.sh/blog/2023/01/vscode-like-emacs/</link><guid isPermaLink="true">https://davi.sh/blog/2023/01/vscode-like-emacs/</guid><description>&lt;p&gt;One of my first posts on this blog in 2020 was about &lt;a href=&quot;https://davi.sh/blog/2020/03/switching-to-emacs/&quot;&gt;my experience switching from VSCode to Emacs&lt;/a&gt; for all of my text/code editing work. I still agree with all of the benefits to Emacs I wrote about back then, and &lt;a href=&quot;https://davi.sh/blog/2022/01/obsidian-zero/&quot;&gt;my posts on Obsidian&lt;/a&gt; show my respect for &lt;code&gt;org-mode&lt;/code&gt; as a format and the ecosystem around it – especially &lt;code&gt;org-agenda&lt;/code&gt;. However, as you might have noticed when the file extension on my posts switched from &lt;code&gt;.org&lt;/code&gt; back to &lt;code&gt;.md&lt;/code&gt;, I’ve moved on from Emacs for now. But I’m happy with how I’ve moved my good habits back to VSCode. Spending time on config gave me more confidence in my ability to configure software I use to my liking, and make software work for me rather than make me work for the software.&lt;/p&gt;
&lt;p&gt;At work, I’m always editing code while SSH’d into a remote machine. &lt;a href=&quot;https://www.gnu.org/software/tramp/&quot;&gt;TRAMP&lt;/a&gt; with &lt;a href=&quot;https://github.com/emacs-lsp/lsp-mode&quot;&gt;&lt;code&gt;lsp-mode&lt;/code&gt;&lt;/a&gt; on a &lt;a href=&quot;https://github.com/mongodb/mongo&quot;&gt;very large C++ codebase&lt;/a&gt; just had too much lag for me. Even accessing Emacs compiled with native-comp running on my remote machine had way too much input delay when waiting on responses from &lt;a href=&quot;https://clangd.llvm.org&quot;&gt;clangd&lt;/a&gt; or &lt;a href=&quot;https://github.com/MaskRay/ccls&quot;&gt;ccls&lt;/a&gt;, two C++ language servers. Now, it’s more than possible I was &lt;a href=&quot;https://www.wired.com/2010/06/iphone-4-holding-it-wrong/&quot;&gt;holding it wrong&lt;/a&gt; – I do have colleagues that work all day in Emacs. But it seems like my bad UX experience was due to the &lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/elisp/Threads.html&quot;&gt;limitations of Emacs’s concurrency support&lt;/a&gt; – calls to language server were blocking other interactions with the UI. On the other side of the hedge, VSCode really just works. I can still type and otherwise interact with the editor while waiting for clangd to process a request. The &lt;a href=&quot;https://code.visualstudio.com/docs/remote/ssh&quot;&gt;remote SSH extension&lt;/a&gt; is the state of the art. The &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd&quot;&gt;clangd integration&lt;/a&gt; was plug-and-play. And I’ve never experienced the lag that I was feeling every day on Emacs.&lt;/p&gt;
&lt;p&gt;While I love the smoothness of the VSCode UI, I’m not a fan of how easy it is fall back on the mouse while navigating around the editor. My favorite part of the &lt;a href=&quot;https://github.com/doomemacs/doomemacs&quot;&gt;Doom Emacs&lt;/a&gt; distribution was the keybindings: it made me feel like I was playing chords on the piano when splitting panes and navigating around. While I’d configured Emacs to look and feel like VSCode in my original Emacs post, I ultimately got rid of the project explorer and visual tabs to embrace more Emacs-y buffer and project management. I found myself reaching for the mouse way more often when I moved back to VSCode, putting myself at risk for RSI and generally interrupting my flow. So I set out to make my VSCode configuration as close to my Doom Emacs setup as possible. There were only a handful of changes I needed to make:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Installed VSCode’s &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=vscodevim.vim&quot;&gt;Vim plugin&lt;/a&gt;. I’ve used vim keybindings all over the place for almost four years at this point.&lt;/li&gt;
&lt;li&gt;Installed &lt;a href=&quot;https://vspacecode.github.io&quot;&gt;VSpaceCode + WhichKey&lt;/a&gt;. Together, these plugins replicate the chords/leader bindings that Doom borrowed from &lt;a href=&quot;https://www.spacemacs.org&quot;&gt;Spacemacs&lt;/a&gt; really well. I’ve &lt;a href=&quot;https://vspacecode.github.io/docs/menu-customization#incrementally&quot;&gt;remapped a few bindings&lt;/a&gt; that were different in Doom and added a few more as well.&lt;/li&gt;
&lt;li&gt;Disabled &lt;a href=&quot;https://code.visualstudio.com/docs/getstarted/userinterface#_tabs&quot;&gt;Tabs&lt;/a&gt;. Tabs are a visual abstraction that are hard to use effectively without reaching for the mouse. I got used to Emacs’s buffer management, where I can fuzzy-search for recently opened files in a reverse-chronological list. VSCode’s analog to buffers are called &lt;a href=&quot;https://code.visualstudio.com/docs/getstarted/userinterface#_open-editors&quot;&gt;Editors&lt;/a&gt; and are normally mapped on to Tabs; after disabling Tabs I can still navigate open Editors with the command “View: Show Editors in Active Group By Most Recently Used”, which I’ve mapped to &lt;code&gt;&amp;#x3C;leader&gt; ,&lt;/code&gt; with VSpaceCode.
&lt;ul&gt;
&lt;li&gt;I removed tabs by adding &lt;code&gt;&quot;workbench.editor.showTabs&quot;: false&lt;/code&gt; to my &lt;code&gt;settings.json&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;The command ID for “Show Editors in Active Group” is &lt;code&gt;workbench.action.showEditorsInActiveGroup&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Disabled the &lt;a href=&quot;https://code.visualstudio.com/docs/getstarted/userinterface#_activity-bar&quot;&gt;Activity Bar&lt;/a&gt;. I can pull up sidebar Views like &lt;code&gt;File Explorer&lt;/code&gt; and &lt;code&gt;Find in Files&lt;/code&gt; with a shortcut, and dismiss them with cmd-B.
&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;&quot;workbench.activityBar.visible&quot;: false&lt;/code&gt; to your &lt;code&gt;settings.json&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The fact that VSCode can do all this is a testament to its extensability and the power of a more limited but still deep &lt;a href=&quot;https://code.visualstudio.com/api&quot;&gt;plugin API&lt;/a&gt;. A &lt;code&gt;settings.json&lt;/code&gt; file is no match for &lt;code&gt;init.el&lt;/code&gt; when it comes to expressiveness, but I’ve still been able to mold VSCode into an editor that I feel at home using.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/images/vscode-screenshot.png&quot; alt=&quot;Configured VSCode Screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;I’m sure I’ll keep playing around with Emacs going forward, but the all-consuming nature of the Emacs philosophy – the idea that everything from email to web browsing to journaling should happen inside Emacs – doesn’t resonate with me in practice. I can use VSCode for most of my development, Obsidian for editing Markdown, and even PyCharm for Python/Django development (check out &lt;a href=&quot;https://github.com/MarcoIeni/intellimacs&quot;&gt;intellimacs&lt;/a&gt; for JetBrains IDEs if you’re interested in my setup there). With just a few hours of set-up, I can use the right tool for the job and still feel at home regardless.&lt;/p&gt;</description><pubDate>Tue, 24 Jan 2023 17:00:00 GMT</pubDate></item><item><title>Improving My Writing Style One Python Script at a Time</title><link>https://davi.sh/blog/2022/12/check-grammar-with-python/</link><guid isPermaLink="true">https://davi.sh/blog/2022/12/check-grammar-with-python/</guid><description>&lt;p&gt;One of my pet peeves about my natural writing style is how I lean into complex sentences divided by commas. Left unchecked, my prose starts looking like it might be ChatGPT’s attempt at writing a blog post in the style of s-expressions. I thought it would be neat to try and write some code to help me proofread for this specific issue and improve my posts.&lt;/p&gt;
&lt;p&gt;So much of my Python experience is from writing apps with Django that I forgot how quick and easy it is to whip up a small script that does some text processing with nothing but the standard library. As much as I appreciate static types and exhaustiveness checking in larger programs, being able to ignore edge cases that I know don’t appear in the specific input I’m concerned with is a relief for scripts like this.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/davish/prose-nits/blob/main/sequential_runons.py&quot;&gt;first script I wrote&lt;/a&gt; looks for sequences of multiple sentences that have too many commas in them. If there are more than three commas in two adjacent sentences, I need to reword something. Here’s its output on one of my first blogposts, &lt;a href=&quot;https://davi.sh/blog/2020/03/switching-to-emacs/&quot;&gt;&lt;em&gt;Switching to Emacs&lt;/em&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;***&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;One thing I started to worry about recently has been my reliance on a closed-source, and, once I&apos;m out of college, pretty expensive editor.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Sure, many employers pay for licenses, but it would be awesome if I could configure an editor that worked well for me and was also completely open-source.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 5 commas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;***&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Sure, many employers pay for licenses, but it would be awesome if I could configure an editor that worked well for me and was also completely open-source.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;A few months ago, I switched my Python development to VSCode, where I had already been doing frontend JS/TypeScript dev for a few years.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 4 commas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;***&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;After learning vim keybindings this summer, I would&apos;ve definitely liked to also be using the keyboard more often.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Primarily, though, I&apos;d heard really cool things about [org-mode](https://orgmode.org/), and wanted to try it out.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 4 commas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;***&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Primarily, though, I&apos;d heard really cool things about [org-mode](https://orgmode.org/), and wanted to try it out.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Full support of org-mode is only present in emacs, which in the midst of quarantine would be a bit of excitement to check out.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 4 commas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;***&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;The Python language server was a bit finnicky, but after setting it up, along with JavaScript, I&apos;m already at a point where I can be productive.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I enabled a few more Doom modules, like a terminal emulator (`term`) and a file explorer (`treemacs`).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 4 commas&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since these windows are all processed in order, I can see without any extra processing that there’s actually two sequences of three sentences with 7 and 6 commas respectively.&lt;/p&gt;
&lt;p&gt;As you might expect, I ran this post through the script before publishing. It had no notes! I’m improving already.&lt;/p&gt;</description><pubDate>Tue, 20 Dec 2022 17:00:00 GMT</pubDate></item><item><title>Mocking Obsidian&apos;s Markdown Parser</title><link>https://davi.sh/blog/2022/12/obsidian-api-mock/</link><guid isPermaLink="true">https://davi.sh/blog/2022/12/obsidian-api-mock/</guid><description>&lt;p&gt;I’ve been working on a plugin for Obsidian called &lt;a href=&quot;https://github.com/davish/obsidian-full-calendar&quot;&gt;Obsidian Full Calendar&lt;/a&gt; on-and-off for the past 10 months or so. For most of that time the plugin has had no unit tests, and I finally got around to adding some test coverage during a big refactor.&lt;/p&gt;
&lt;p&gt;Tests are easiest when code doesn’t have side effects since filesystems and network calls often aren’t available in the environment the tests are running in. Obsidian’s core code is closed-source and can only be run from inside the Electron app, so plugin developers who want test coverage aren’t left with many options but to test their plugins completely outside of Obsidian. Unfortunately for me, my plugin is mostly a pile of glue sitting between &lt;a href=&quot;https://fullcalendar.io&quot;&gt;FullCalendar&lt;/a&gt; as the view layer and the Obsidian filesystem APIs for persistence. I would need to mock out the relevant APIs from Obsidian if I wanted to have any meaningful test coverage of my own code.&lt;/p&gt;
&lt;p&gt;There isn’t yet any comprehensive mock Obsidian API for use in a testing environment that I could reach for, so I went ahead writing my own!&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id=&quot;taking-stock&quot;&gt;Taking Stock&lt;/h2&gt;
&lt;p&gt;There are two Obsidian APIs that I make heavy use of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Vault&lt;/code&gt;: The filesystem API. This API allows you to create, read, update and delete files. The core type that is passed around these APIs is &lt;code&gt;TAbstractFile&lt;/code&gt;, which can either be a &lt;code&gt;TFolder&lt;/code&gt; or a &lt;code&gt;TFile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MetadataCache&lt;/code&gt;: To support everything from file search to graph view to the backlink count, Obsidian parses every Markdown file in a Vault and saves relevant information about its structure in the &lt;code&gt;MetadataCache&lt;/code&gt;. I’m most interested in:
&lt;ul&gt;
&lt;li&gt;YAML frontmatter, parsed into a dictionary&lt;/li&gt;
&lt;li&gt;Position and task status of lists and list items&lt;/li&gt;
&lt;li&gt;Position and text of Markdown headings&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;first-thoughts&quot;&gt;First Thoughts&lt;/h2&gt;
&lt;p&gt;Mocking out relevant parts of the &lt;code&gt;Vault&lt;/code&gt; API isn’t that bad, but keeping a mock MetadataCache that was in sync with file contents felt like it was going to be a big pain. Every item in a cache entry records its location in its file, consisting of line/column information as well as the character offset. Writing cache entries by hand would require recording the position of every single heading and list item within the file – a single character change would have a cascading effect on the whole cache entry.&lt;/p&gt;
&lt;p&gt;My first thought to avoid this was just to write a simplified metadata parser for testing purposes. My mock API would just take in file contents and would parse out the metadata itself. I wasn’t particularly in the mood to write a Markdown parser just for unit tests. A library like &lt;a href=&quot;https://github.com/syntax-tree/mdast&quot;&gt;&lt;code&gt;mdast&lt;/code&gt;&lt;/a&gt; might have made this easier, but that can be a rabbit hole for another day.&lt;/p&gt;
&lt;h2 id=&quot;the-filebuilder&quot;&gt;The &lt;code&gt;FileBuilder&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The solution I landed on is to build up a file’s contents line-by-line while also constructing the metadata for each line at the same time. It’s sort of the inverse of parsing – the file is constructed in a structured format that’s easy to write, and then serialized to a string &lt;em&gt;and&lt;/em&gt; to the metadata format at the same time! The resulting builder DSL is super readable for the verbosity of the output it produces.&lt;/p&gt;
&lt;p&gt;The following expression:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;contents&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;metadata&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; FileBuilder&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;frontmatter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ hello: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;world&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;heading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Journal&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;this is a journal entry!&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;and a second line!&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;heading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;My list&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;list&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ListBuilder&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;first list item&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;second list item&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;to-do&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;done&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;nested list&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;list&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ListBuilder&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;nested list item&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;another nested item&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;done&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Renders this Markdown:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;hello: world&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;## Journal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;this is a journal entry!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;and a second line!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;## My list&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- first list item&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- second list item&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- [ ] to-do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- [x] done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- nested list&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	- nested list item&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	- another nested item&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;details&gt;
&lt;summary&gt;And constructs the following metadata, which I&apos;ll hide in a dropdown.&lt;/summary&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;frontmatter&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;hello&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;world&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;position&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;end&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;headings&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;position&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;21&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;end&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;31&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;heading&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Journal&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;level&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;position&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;76&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;end&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;86&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;heading&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;My list&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;level&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;listItems&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;position&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;7&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;87&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;end&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;7&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;17&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;104&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;parent&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;-7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;position&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;105&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;end&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;18&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;123&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;parent&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;-7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;position&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;9&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;124&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;end&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;9&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;11&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;135&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;parent&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;-7&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;task&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;position&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;136&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;end&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;146&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;parent&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;-7&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;task&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;position&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;11&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;147&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;end&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;11&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;13&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;160&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;parent&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;-7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;position&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;12&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;163&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;end&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;12&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;22&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;183&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;parent&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;11&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;position&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;13&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;186&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;end&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;line&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;13&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;25&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;209&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;parent&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;11&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2 id=&quot;testing-the-test-code&quot;&gt;Testing the test code&lt;/h2&gt;
&lt;p&gt;I’m never sure of what the best practice is for testing code that you’ve written for the purpose of testing, but this case I’d constructed a complex enough API and written enough code that I decided to see how deep this rabbit hole goes.&lt;/p&gt;
&lt;p&gt;The main thing I wanted to avoid was manually writing out the output that I was expecting. Constructing metadata by hand was what I wanted to avoid in the first place, and I didn’t want to track down small typos in my transcription of the Markdown documents. I came up with a testing strategy that minimized the amount of typing I had to do for each of the output formats, allowing me to add a lot of test cases with minimal effort.&lt;/p&gt;
&lt;h3 id=&quot;text-output&quot;&gt;Text output&lt;/h3&gt;
&lt;p&gt;Jest comes with built-in &lt;a href=&quot;https://jestjs.io/docs/snapshot-testing&quot;&gt;snapshot testing functionality&lt;/a&gt; that allows test authors to simply visually validate the test output after the test is run the first time. On subsequent runs, the result is compared to the accepted output to make sure there isn’t any regression.&lt;/p&gt;
&lt;p&gt;We get the option of using separate snapshot files or &lt;a href=&quot;https://jestjs.io/docs/snapshot-testing#inline-snapshots&quot;&gt;keeping the snapshots inline&lt;/a&gt;. I opted for inline snapshots since the output isn’t particularly long for any given test. Here’s an example of what the inline snapshot assertion looks like after it’s accepted:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;contents&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;metadata&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; FileBuilder&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;heading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;First heading&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;heading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Second heading&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;heading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Third heading&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;done&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;expect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(contents).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &quot;## First heading&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  ## Second heading&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  ## Third heading&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;parsed-metadata&quot;&gt;Parsed metadata&lt;/h3&gt;
&lt;p&gt;The goal of this whole builder is to make sure that the metadata produced is identical to the metadata that Obsidian constructs from parsing a Markdown file. To verify that, I wanted to use Obsidian’s actual parser output as the source-of-truth for these tests.&lt;/p&gt;
&lt;p&gt;I copy/pasted the inline snapshot result for every test into a blank Obsidian note and added a small Obsidian command to my local copy of my plugin to grab the current note’s metadata and copy the full assertion to the clipboard:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;addCommand&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  id: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;full-calendar-FCTEST&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  name: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;FCTEST&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  callback&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; leaf&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.app.workspace.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getMostRecentLeaf&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (leaf.view &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;instanceof&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; MarkdownView&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; file&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; leaf.view.file;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; cache&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        ...&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.app.metadataCache.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getFileCache&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(file),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      delete&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; cache[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;sections&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (cache) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(cache);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; navigator.clipboard.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;writeText&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          `assert.deepStrictEqual(metadata, ${&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;cache&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;})`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding an assertion for metadata to each test only took a copy-paste and five or so keypresses!&lt;/p&gt;
&lt;p&gt;The code for this is still on an &lt;a href=&quot;https://github.com/davish/obsidian-full-calendar/tree/feature/cache/src/test_helpers&quot;&gt;in-progress branch&lt;/a&gt; in Obsidian Full Calendar, but I think it would be generally usable for other plugins, so I might split it out into a separate package in the future.&lt;/p&gt;</description><pubDate>Tue, 20 Dec 2022 17:00:00 GMT</pubDate></item><item><title>Deploy a Django app with fly.io</title><link>https://davi.sh/blog/2022/10/django-with-flyio/</link><guid isPermaLink="true">https://davi.sh/blog/2022/10/django-with-flyio/</guid><description>&lt;p&gt;I started a new project a few days ago built with Django. &lt;a href=&quot;https://techcrunch.com/2022/08/25/heroku-announces-plans-to-eliminate-free-plans-blaming-fraud-and-abuse/&quot;&gt;Heroku is killing their free tier&lt;/a&gt;, and I’ve read that &lt;a href=&quot;https://xeiaso.net/blog/fly.io-heroku-replacement&quot;&gt;Fly.io is the cool new thing&lt;/a&gt;, so I decided to try it out.&lt;/p&gt;
&lt;p&gt;Overall, Fly has great documentation. Their &lt;a href=&quot;https://fly.io/docs/languages-and-frameworks/&quot;&gt;Language and Framework Guides&lt;/a&gt; are pretty comprehensive, but in a list that includes popular frameworks like Rails and Laravel alongside &lt;a href=&quot;https://mcfunley.com/choose-boring-technology&quot;&gt;less boring&lt;/a&gt; options like RedwoodJS and Remix, I couldn’t help but notice Django’s conspicuous absence.&lt;/p&gt;
&lt;p&gt;I was able to get everything working great with Django and Fly.io after some trial and error, so I wanted to write up my process to make it easier for people going forward.&lt;/p&gt;
&lt;!--more--&gt;
&lt;hr&gt;
&lt;p&gt;If you want to get everything set up when starting a new project, use the &lt;code&gt;startproject&lt;/code&gt; template I made which you can find at &lt;a href=&quot;https://github.com/davish/django-flyio-template&quot;&gt;github.com/davish/django-flyio-template&lt;/a&gt;. Read on if you’re interested in a more in-depth look!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;As opionated as Django is, there’s still some choices you need to make when starting a project. Here’s my stack that I’ll use throughout this post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Poetry&lt;/strong&gt; for package management.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Daphne&lt;/strong&gt; for the production webserver.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker&lt;/strong&gt; to describe the deployment.
&lt;ul&gt;
&lt;li&gt;Fly also has &lt;a href=&quot;https://fly.io/docs/reference/builders/#buildpacks&quot;&gt;support for heroku-style buildpacks and Procfiles&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Postgres&lt;/strong&gt; for the production database, since Fly has &lt;a href=&quot;https://fly.io/docs/reference/postgres/&quot;&gt;built-in support for Postgres&lt;/a&gt;.
&lt;ul&gt;
&lt;li&gt;Local development still uses &lt;strong&gt;SQLite&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;0-initialize-the-poetry-project-and-django-app&quot;&gt;0. Initialize the Poetry project and Django App&lt;/h2&gt;
&lt;p&gt;If you’re starting an app from scratch, then run these commands from inside an empty directory where you want to store your project:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; poetry&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; init&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; poetry&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; django&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dj-database-url&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; daphne&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; psycopg2-binary&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; poetry&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; django-admin&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; startproject&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;projec&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;t&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;1-add-the-dockerfile&quot;&gt;1. Add the Dockerfile&lt;/h2&gt;
&lt;p&gt;Create a file called &lt;code&gt;Dockerfile&lt;/code&gt; in the project’s base directory:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;docker&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; python:3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;ENV&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; PYTHONUNBUFFERED 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;WORKDIR&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;COPY&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; poetry.lock pyproject.toml /app/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; pip3 install poetry&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; poetry install --no-root&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;COPY&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; . .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;ENV&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; DJANGO_SETTINGS_MODULE &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&amp;#x3C;project&gt;.settings&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;ENV&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; DJANGO_SECRET_KEY &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;this is a secret key for building purposes&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; poetry run python manage.py collectstatic --noinput&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;CMD&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; poetry run daphne -b 0.0.0.0 -p 8080 &amp;#x3C;project&gt;.asgi:application&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s also important to also add a &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/#dockerignore-file&quot;&gt;&lt;code&gt;.dockerignore&lt;/code&gt; file&lt;/a&gt;, especially if you’re storing your site in a Git repository.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.git/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;__pycache__/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fly.toml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;*.sqlite3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Files that store secrets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.env&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# macOS file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.DS_Store&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Editor-specific configuration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.idea/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.vscode&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;2-configure-the-flyio-app&quot;&gt;2. Configure the Fly.io app&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;flyctl launch --no-deploy&lt;/code&gt; to create a project and get a name and URL. &lt;code&gt;--no-deploy&lt;/code&gt; prevents &lt;code&gt;flyctl&lt;/code&gt; from deploying our app right out of the gate. We have to make some more changes before we can do that.&lt;/li&gt;
&lt;li&gt;The last command will have generated a &lt;code&gt;fly.toml&lt;/code&gt; file. Open it up and add these lines, starting with the existing empty &lt;code&gt;[env]&lt;/code&gt;block:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;toml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  DJANGO_SETTINGS_MODULE = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&amp;#x3C;project&gt;.settings&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;deploy&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  release_command = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;poetry run python manage.py migrate&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;[[&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;statics&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  guest_path = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/app/static&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  url_prefix = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/static&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;3-link-a-postgres-database&quot;&gt;3. Link a Postgres Database&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; flyctl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; postgres&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; create&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Make a note of the db app name you choose.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; flyctl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; postgres&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; attach&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;db-nam&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# db-name should be what you selected in the previous step.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;postgres attach&lt;/code&gt; subcommand creates a properly permissioned database user and generates a connection string that’s stored as a &lt;a href=&quot;https://fly.io/docs/reference/secrets/&quot;&gt;secret&lt;/a&gt; named &lt;code&gt;DATABASE_URL&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;4-configure-django-settings&quot;&gt;4. Configure Django settings&lt;/h2&gt;
&lt;p&gt;Back in our Django project, there’s a bit of configuration that we’ll need to do in the project’s &lt;code&gt;settings.py&lt;/code&gt; file.&lt;/p&gt;
&lt;h3 id=&quot;the-databases-dictionary&quot;&gt;The &lt;code&gt;DATABASES&lt;/code&gt; dictionary&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; os&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; dj_database_url&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;DATABASES&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &quot;default&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: dj_database_url.config(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;        default&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;sqlite:///&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; os.path.join(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;BASE_DIR&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;db.sqlite3&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jazzband/dj-database-url&quot;&gt;&lt;code&gt;dj-database-url&lt;/code&gt;&lt;/a&gt; will pick up the connection string that &lt;code&gt;flyctl postgres attach&lt;/code&gt; set in our environment and properly configure our database connection for us. To avoid dealing with Postgres locally, however, we’ll default to a SQLite database when we don’t have a database URL defined.&lt;/p&gt;
&lt;h3 id=&quot;static-files&quot;&gt;Static files&lt;/h3&gt;
&lt;p&gt;Django collects static files through the &lt;code&gt;collectstatic&lt;/code&gt; command that we run in our Docker build process. We both need to tell Django where to store static files on the filesystem (&lt;code&gt;STATIC_ROOT&lt;/code&gt;) and at what relative path these static files will be served from (&lt;code&gt;STATIC_URL&lt;/code&gt;). These need to match The &lt;code&gt;guest_path&lt;/code&gt; and &lt;code&gt;url_prefix&lt;/code&gt; from our &lt;code&gt;fly.toml&lt;/code&gt; file, respectively. &lt;code&gt;guest_path&lt;/code&gt; is &lt;code&gt;/app/static&lt;/code&gt; because we placed our app to the &lt;code&gt;/app&lt;/code&gt; directory in our Dockerfile.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;STATIC_URL&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;static/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;STATIC_ROOT&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;static&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With Heroku and some other hosting providers, you’d have to set up and configure &lt;a href=&quot;http://whitenoise.evans.io/en/stable/&quot;&gt;WhiteNoise&lt;/a&gt; and serve static files through Django. I think it’s great that Fly makes it so easy to bypass the running web server and serve static files directly with so little configuration. It’s a nice bonus that it maps so cleanly on to Django’s own configuration as well.&lt;/p&gt;
&lt;h3 id=&quot;allowed-hosts&quot;&gt;Allowed hosts&lt;/h3&gt;
&lt;p&gt;Run &lt;code&gt;flyctl status&lt;/code&gt; if you’ve forgotten what domain Fly.io has set aside for your app. Once you’ve got that hostname, set these two settings:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ALLOWED_HOSTS&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;hostname.fly.dev&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;CSRF_TRUSTED_ORIGINS&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;https://hostname.fly.dev&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;generate-a-new-secret-key&quot;&gt;Generate a new secret key&lt;/h3&gt;
&lt;p&gt;Django generates a secret key by default and stores it in plaintext in your settings file. This is dangerous in production, since secrets should never be stored in source files where they can get caught up in verison control. As its name implies, &lt;a href=&quot;https://docs.djangoproject.com/en/4.1/ref/settings/#secret-key&quot;&gt;secret keys should be kept secret&lt;/a&gt;, and are important for validating session cookies and other cryptographic uses throughout the framework.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;SECRET_KEY&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; os.getenv(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;DJANGO_SECRET_KEY&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then run:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;flyctl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; secrets&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; set&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; DJANGO_SECRET_KEY=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;generated&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; secret&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ke&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can store any other sensitive values in Fly’s secrets — check out &lt;a href=&quot;https://fly.io/docs/reference/secrets/&quot;&gt;the docs&lt;/a&gt; for more info.&lt;/p&gt;
&lt;h2 id=&quot;5-prepare-for-deploy&quot;&gt;5. Prepare for deploy&lt;/h2&gt;
&lt;p&gt;Fly.io will migrate our database for us on every deploy, so now is a good time to make any changes to your app locally that you’d like to have before that first migration. Most notably, this will likely include a &lt;a href=&quot;https://docs.djangoproject.com/en/3.2/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project&quot;&gt;custom User model&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you’re adding configuration to an existing Django app, there’s probably nothing to be done here.&lt;/p&gt;
&lt;h2 id=&quot;6-deploy-to-flyio&quot;&gt;6. Deploy to fly.io&lt;/h2&gt;
&lt;p&gt;We’re finally ready to deploy our app! Run &lt;code&gt;flyctl deploy --local-only&lt;/code&gt;, and the tool will build your Dockerfile and push it up to Fly.io. I needed to use &lt;code&gt;--local-only&lt;/code&gt; because the remote builder in my account had shut down and I had no way of turning it back on, but your mileage may vary. Local builds require Docker to be installed and running locally.&lt;/p&gt;
&lt;p&gt;Try visiting your site, and see if you can see the default Django welcome page!&lt;/p&gt;
&lt;h2 id=&quot;7-create-admin-user&quot;&gt;7. Create admin user&lt;/h2&gt;
&lt;p&gt;The last thing that you’ll need to do is &lt;a href=&quot;https://docs.djangoproject.com/en/4.1/ref/django-admin/#createsuperuser&quot;&gt;create a superuser&lt;/a&gt; for accessing &lt;a href=&quot;https://docs.djangoproject.com/en/4.1/ref/contrib/admin/&quot;&gt;Django’s built-in admin site&lt;/a&gt;. Luckily, we can run any management commands against our production server just by &lt;code&gt;ssh&lt;/code&gt;ing right into the box:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; flyctl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ssh&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; console&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# From inside the ssh session, run:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; cd app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; poetry run python manage.py createsuperuser&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;And that’s it! You should have a fully functional Django app on Fly.io with a Postgres database, and hopefully you learned some useful &lt;code&gt;flyctl&lt;/code&gt; commands along the way.&lt;/p&gt;
&lt;p&gt;What I’ve presented here is a “minimum viable deployment”, but to make sure your deployment is safe for users and not vulnerable to attacks, it’s important to look through Django’s &lt;a href=&quot;https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/&quot;&gt;deployment checklist&lt;/a&gt;. Some things have already been addressed throughout this article, but items like the &lt;code&gt;DEBUG&lt;/code&gt; setting are important to be aware of for production Django.&lt;/p&gt;</description><pubDate>Tue, 04 Oct 2022 17:00:00 GMT</pubDate></item><item><title>Hello, Solar System!</title><link>https://davi.sh/blog/2022/10/hello-solar-system/</link><guid isPermaLink="true">https://davi.sh/blog/2022/10/hello-solar-system/</guid><description>&lt;p&gt;Hello World, &lt;a href=&quot;/blog/2020/02/my-first-post&quot;&gt;again&lt;/a&gt;! I’ve rewritten this website using the &lt;a href=&quot;https://astro.build&quot;&gt;Astro web framework&lt;/a&gt;. Astro’s &lt;em&gt;very&lt;/em&gt; new — it had its &lt;a href=&quot;https://astro.build/blog/astro-1/&quot;&gt;1.0 release&lt;/a&gt; just under two months ago. There’s three things that got me excited enough about it to rewrite my whole site.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JSX&lt;/strong&gt;. &lt;a href=&quot;https://reactjs.org/docs/introducing-jsx.html&quot;&gt;JSX&lt;/a&gt; is best known as the templating syntax that powers React. While Astro &lt;em&gt;can&lt;/em&gt; integrate with &lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/react/&quot;&gt;React&lt;/a&gt;, &lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/vue/&quot;&gt;Vue&lt;/a&gt;, &lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/svelte/&quot;&gt;Svelte&lt;/a&gt;, or any other framework from &lt;a href=&quot;https://docs.astro.build/en/core-concepts/framework-components/&quot;&gt;a growing list&lt;/a&gt; , &lt;a href=&quot;https://docs.astro.build/en/core-concepts/astro-components/&quot;&gt;&lt;code&gt;.astro&lt;/code&gt; templates&lt;/a&gt; are written with a mix of TypeScript and JSX and are rendered directly to vanilla HTML with no JavaScript at runtime. TypeScript is written in a &lt;a href=&quot;https://docs.astro.build/en/core-concepts/astro-components/#the-component-script&quot;&gt;preamble section&lt;/a&gt; that looks similar to Markdown frontmatter and sets up the scope for the rest of the template. I found Hugo’s templating system that is built directly on &lt;a href=&quot;https://pkg.go.dev/text/template&quot;&gt;Go’s templating library&lt;/a&gt; to be very limiting by contrast. At this point, I definitely believe the adage that “any powerful templating language eventually grows into an awkward programming language” — I’d much rather use a full programming language as my templating language.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scoped CSS&lt;/strong&gt;. CSS written in &lt;code&gt;.astro&lt;/code&gt; templates are scoped to that template only. I’m not a fan of &lt;a href=&quot;https://adamwathan.me/css-utility-classes-and-separation-of-concerns/&quot;&gt;dogmatic separation of concerns in CSS&lt;/a&gt;. I also don’t have much experience with Tailwind, and I enjoy writing SCSS. So scoped styles in all my templates is a happy medium where my styles live close to my templates while I can write in a familiar syntax.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lightweight&lt;/strong&gt;. With everything from archetypes to taxonomies, Hugo is really a static CMS more than a blog generator. It’s a much more general system than I need. Astro is a bit “closer to the metal” in that respect. When I’ve had to write some helpers that I took for granted in Hugo, writing custom helpers is much easier since it can be accomplished in a few lines of Typescript.&lt;/li&gt;
&lt;/ol&gt;
&lt;!--more--&gt;
&lt;p&gt;Astro is sponsored by &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt;, and in many ways it looks like their answer to &lt;a href=&quot;https://vercel.com/solutions/nextjs&quot;&gt;Vercel&lt;/a&gt;’s extremely popular &lt;a href=&quot;https://nextjs.org/&quot;&gt;NextJS&lt;/a&gt; framework. NextJS is frontend-first and dynamic by default while it puts the option to &lt;a href=&quot;https://nextjs.org/docs/advanced-features/static-html-export&quot;&gt;export to static HTML&lt;/a&gt; off to the side. By contrast, Astro is &lt;a href=&quot;https://docs.astro.build/en/concepts/why-astro/#server-first&quot;&gt;server-first&lt;/a&gt; by default, making you opt-in to &lt;a href=&quot;https://docs.astro.build/en/guides/server-side-rendering/&quot;&gt;server-side runtime rendering&lt;/a&gt; and &lt;a href=&quot;https://docs.astro.build/en/concepts/islands/&quot;&gt;client-side reactive “Islands”&lt;/a&gt;. It’s an approach that was very attractive for building a static site, and I’m interested to see how they plan to evolve the framework from here.&lt;/p&gt;
&lt;p&gt;The transition from Hugo to Astro wasn’t too bad since I was able to find a 1-1 mapping between my Hugo templates and partials with Astro components. I’d like to give a special shoutout to &lt;a href=&quot;https://www.brycewray.com&quot;&gt;Bryce Wray&lt;/a&gt;, whose &lt;a href=&quot;https://github.com/brycewray/astro-site&quot;&gt;site repository&lt;/a&gt; and blog posts on Astro were a great reference when building out my own site. While the Astro docs are great, they are also new, and there’s a few things that I was able to find out about by snooping around Bryce’s repo.&lt;/p&gt;
&lt;p&gt;I’ll miss Hugo’s support for rendering &lt;code&gt;.org&lt;/code&gt; files – I’ve unfortunately had to convert posts from &lt;a href=&quot;/blog/2020/03/switching-to-emacs&quot;&gt;my time using emacs&lt;/a&gt; to Markdown using &lt;a href=&quot;https://pandoc.org&quot;&gt;pandoc&lt;/a&gt;. For the time being, I’m stuck opening Chrome when working on the site because Astro’s dev server doesn’t hot refresh properly in Safari. Besides these two nitpicks, I’m overall happy with the move. I think it’ll help me make new additions to this site over time with less friction than before. We’ll see how that goes!&lt;/p&gt;</description><pubDate>Mon, 03 Oct 2022 17:00:00 GMT</pubDate></item><item><title>Renting in New York City</title><link>https://davi.sh/blog/2022/05/renting-in-nyc/</link><guid isPermaLink="true">https://davi.sh/blog/2022/05/renting-in-nyc/</guid><description>&lt;p&gt;I just signed a lease for my second apartment since moving to New York City after college. Some friends asked me for advice on how to find an apartment here, and I didn’t realize at first how many opinions I have on the whole process. I’ve tried to distill my thoughts and approach here.&lt;/p&gt;
&lt;p&gt;Renting an apartment in New York is all about tradeoffs, mostly between time and money. If you’re willing to spend more time seeing apartments in person, then you’re much more likely to find a great apartment that others are overlooking because the information online is incomplete or hard to find. If you’re willing to spend more money or give up something that most other renters are looking for, you can find a nicer apartment without searching for a diamond in the rough.&lt;/p&gt;
&lt;p&gt;If you can put in the time to develop an intuition for the types of apartments on the market in your budget, you’ll be much more confident in your decision when you ultimately sign a lease.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;NYC’s rental market is famously crazy, and the disruptions brought by COVID-19 haven’t made it any less so. Renters were often able to negotiate their rent downward or sign new leases with attractive deals during 2020 and the first few months of 2021, but since last summer, rents have &lt;a href=&quot;https://www.globest.com/2021/08/25/new-york-passes-san-francisco-as-the-most-expensive-rental-market/?slreturn=20220404122002&quot;&gt;bounced back and then some&lt;/a&gt;. A lot of people have since been priced out of their current apartments, which only adds to how much demand there is in the market right now.&lt;/p&gt;
&lt;p&gt;New York is a huge city, and I’m an early-career software engineer who works in Midtown Manhattan looking for apartments in Lower Manhattan and adjacent parts of Brooklyn. I also happen to be a bit of a recovering HGTV junkie who enjoys looking at floorplans more than is probably healthy. I can’t expect this post to cover everyone’s rental experience, but hopefully there are some small bits of advice you can pull from it.&lt;/p&gt;
&lt;h2 id=&quot;when-to-begin-your-search&quot;&gt;When to begin your search&lt;/h2&gt;
&lt;p&gt;The first listings for a given move-in date will start getting posted 1.5 months before, and new ones keep appearing up to as little as a week before move-in. It can be really useful to start looking at the apartments that are available in the month before your target move. This helps you understand what’s on the market in your price range and develop the intuition to spot a good deal when it presents itself.&lt;/p&gt;
&lt;h2 id=&quot;start-on-streeteasy&quot;&gt;Start on StreetEasy&lt;/h2&gt;
&lt;p&gt;How do you even find apartments that are available to rent? &lt;a href=&quot;https://streeteasy.com&quot;&gt;StreetEasy&lt;/a&gt; is your best friend. It’s a listing website exclusive to NYC where brokers generally post all their listings. You can create an account and save searches to come back to and get email updates for.&lt;/p&gt;
&lt;p&gt;I recommend taking a visit to the &lt;a href=&quot;https://streeteasy.com/nyc/user/email_preferences&quot;&gt;“Email Preferences” section of your account settings&lt;/a&gt;. I set all my saved searches to send emails once per day, and also I checked the setting to combine all the searches into one email. This meant that every morning I’d get a digest of all the new apartments that were listed within my price range and desired locations.&lt;/p&gt;
&lt;p&gt;I got into the habit of checking this email every morning before starting work, and clicking the “Request a Tour” button on every listing that was interesting to me. Brokers usually reach out with a day or two.&lt;/p&gt;
&lt;h2 id=&quot;beyond-streeteasy&quot;&gt;Beyond StreetEasy&lt;/h2&gt;
&lt;p&gt;StreetEasy makes it super simple to find listings and contact listing agents, but it isn’t the whole process. Forming too much of an opinion based off a StreetEasy listing can lead you to miss out on a great apartment.&lt;/p&gt;
&lt;p&gt;The listing for my first apartment only had a photo for one out of its four bedrooms. The listing for my second apartment &lt;em&gt;had no photos of the apartment itself&lt;/em&gt;, just photos of the shared rooftop and the mail room. In both cases, the listing agents weren’t being malicious. They were just working with old and incomplete photos and couldn’t take new ones with tenants still living in the apartment.&lt;/p&gt;
&lt;p&gt;My second apartment was far-and-away better than most others I’d seen, but it had an order of magnitude fewer “likes” on StreetEasy. Only one other person showed up to the open house compared to the dozens of people I had seen at a very well-photographed apartment around the corner a few weeks before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don’t be afraid of going to listings without many photos&lt;/strong&gt;! Sometimes agents just haven’t been able to take them yet, and you’ll be able to see a place before other renters start taking it seriously. You still need to do your due dilligence. Check out the building on Street View or walk by in person if it’s convenient for you. I also would &lt;strong&gt;NOT&lt;/strong&gt; recommend signing for an apartment without any pictures at all if you’re not able to visit in person.&lt;/p&gt;
&lt;p&gt;On the other side of the coin, it’s important to recognize that apartments with tons of photos aren’t always represented more accurately. Misleading camera angles and different lighting choices can make apartments look a lot better on your screen than they do in real life. Visiting apartments in person can help you learn how photos map to reality and set expectations more appropriately.&lt;/p&gt;
&lt;p&gt;In a similar vein, I have a friend living in Boston who rented his apartment off of Craigslist, which lots of people consider a less legitimate website than StreetEasy or Zillow. He was able to find a great apartment that others were discounting for more superficial reasons.&lt;/p&gt;
&lt;h2 id=&quot;talk-to-listing-agents&quot;&gt;Talk to listing agents&lt;/h2&gt;
&lt;p&gt;When you visit an apartment, make sure to talk to the agent and let them know what you’re looking for. It’s pretty likely they’ll be trying to rent out other apartments, and sometimes, they’ll show you places that haven’t been listed yet. Ultimately, real estate agents earn their income on commission, and so they’ll do what they can to help you find an apartment where they can get the broker’s fee. Getting sneak previews like this is a great way to get a head start on other renters.&lt;/p&gt;
&lt;h3 id=&quot;asking-questions&quot;&gt;Asking questions&lt;/h3&gt;
&lt;p&gt;It’s important to ask questions to the listing agent about the apartment while you’re touring. Here are some that I’ve found useful or illuminating.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What utilities are included, and which are my responsibility to pay for?&lt;/li&gt;
&lt;li&gt;Does the super live in the building?&lt;/li&gt;
&lt;li&gt;[If there isn’t central air conditioning] are A/C units included, or will I need to buy and bring my own?&lt;/li&gt;
&lt;li&gt;[If you want to apply] What will the application process look like?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the current tenants are present during the showing, don’t be shy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How has your experience living here been?&lt;/li&gt;
&lt;li&gt;Why are you moving out?&lt;/li&gt;
&lt;li&gt;How responsive or helpful is the super and building management?&lt;/li&gt;
&lt;li&gt;What has been your most/least favorite part of living here?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lots of tenants give candid answers that can be super helpful in your decision process.&lt;/p&gt;
&lt;h2 id=&quot;applying&quot;&gt;Applying&lt;/h2&gt;
&lt;p&gt;Once you find an apartment you like, the next step is to apply! Tenant rights are strong in New York, so building management companies do a lot of due dilligence to make sure you can really pay rent before drawing up a lease for you to sign.&lt;/p&gt;
&lt;p&gt;My main advice through the application process is to be as responsive as possible and be honest with the leasing agent and building management. Before you sign the lease there’s nothing legally requiring them to continue working with you, so try to get requested documents to them as quickly as you can. It’s also a good way to make a positive first impression on your potential your future landlord.&lt;/p&gt;
&lt;p&gt;The first type of application I’ve encountered has you pay a deposit that’s anywhere from $500 to a month’s worth of rent. This will be refunded if your application is rejected by management. While it might seem really burdensome to give up money before even having a lease to sign, the apartment will likely be taken off the market while your application is being evaluated. This is great when you find a place you really like – it means that you don’t need to worry about competing offers, or other renters applying who are more attractive tenants for whatever reason.&lt;/p&gt;
&lt;p&gt;This most recent search, I ran into a lot of applications that didn’t require a fee. Instead, the listing agent collected as many applications as came in after an open house, and management picked the one they like the best. This seems nice because there’s no upfront cost, but there’s generally more waiting since building management has more applications to read, and there’s no guarantee that you’ll get the apartment even if you have a great application. I submitted and was rejected in three “no fee” applications at different apartments before eventually having an application accepted along with a deposit.&lt;/p&gt;
&lt;h2 id=&quot;signing-the-lease&quot;&gt;Signing the lease&lt;/h2&gt;
&lt;p&gt;Congrats! Your application’s been approved. Now it’s time to sign a lease. I don’t really have many opinions on this part of the process. I’m also certainly not a lawyer, but one thing I will say is make sure to actually read your lease! Even if you don’t want to negotiate anything inside it, it is always good to know what you’re signing.&lt;/p&gt;
&lt;p&gt;Now it’s time to relax before packing up and moving. Good work, and good luck in the new place!&lt;/p&gt;</description><pubDate>Sun, 08 May 2022 17:00:00 GMT</pubDate></item><item><title>Task Management in Obsidian</title><link>https://davi.sh/blog/2022/01/obsidian-one/</link><guid isPermaLink="true">https://davi.sh/blog/2022/01/obsidian-one/</guid><description>&lt;p&gt;Obsidian is first and foremost a Markdown editor with first-class support for internal links between notes. But just because it wasn’t built as a to-do app, doesn’t mean it can’t become one. Community plugins and external tools have made Obsidian work just as well for me as any task management app I’ve used in the past. As an added bonus, because I’m keeping notes about my day and what I’m reading, it’s easy for me to keep tasks in the context of the thoughts that spawned them.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Plugins:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/schemar/obsidian-tasks&quot;&gt;Tasks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Vinzent03/obsidian-advanced-uri&quot;&gt;Advanced URI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mgmeyers/obsidian-kanban&quot;&gt;Kanban&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kepano/obsidian-minimal&quot;&gt;Minimal Theme&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;External Tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://apps.apple.com/us/app/shortcuts/id915249334&quot;&gt;Siri Shortcuts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;agenda-view&quot;&gt;Agenda View&lt;/h2&gt;
&lt;p&gt;Like I mentioned in &lt;a href=&quot;https://davi.sh/blog/2022/01/obsidian-zero/&quot;&gt;Part Zero&lt;/a&gt;, the first real productivity workflow that clicked for me was &lt;a href=&quot;https://orgmode.org/manual/Agenda-Views.html&quot;&gt;Org Mode’s agenda system&lt;/a&gt;. My tasks could stay in the context of the notes and project that spawned them in the first place, but could be collected together into a global to-do list on command by calling up the agenda buffer. I’ve tried to replicate this quite closely in Obsidian.&lt;/p&gt;
&lt;p&gt;By default, Obsidian doesn’t have any real organization for tasks beyond Markdown support for rendering checkboxes and a special &lt;a href=&quot;https://help.obsidian.md/Plugins/Search#Search+operators&quot;&gt;filter in the core Search plugin&lt;/a&gt;. There’s a handful of plugins that can help you collect tasks in a more global list, but I chose towards &lt;a href=&quot;https://github.com/schemar/obsidian-tasks&quot;&gt;Obsidian Tasks&lt;/a&gt; because of its custom query block. In any note, I can add a code block with the special type &lt;code&gt;tasks&lt;/code&gt; and create an ad-hoc to-do list for tasks that satisfied a given query. Combining a few of these queries, I put together a central “Agenda Note” that contains my entire to-do list, at a glance. This is what it looks like right now:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/images/obsidian-agenda.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
And you can click here to reveal the full source code.
&lt;/summary&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;### Overdue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```tasks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;not done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;due before today&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;### Due today&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```tasks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;not done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;due today&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;### Due in the next two weeks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```tasks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;not done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;due after today&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;due before in two weeks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;### No due date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```tasks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;not done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;no due date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;### Done today&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```tasks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;done today&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All in all, pretty easy to understand! The queries look very close to natural language.&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;I also give myself a view of where I stand on tasks that are important for today in each daily note. I’ll dive deeper into my daily notes next time, but here’s a snippet from that template:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## To Do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```tasks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;not done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;due before {{date}}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```tasks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;due on {{date}}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These two queries let me see everything I should handle today: overdue tasks “roll over” automatically from previous days. I also keep a record of tasks done on each specific day in my daily notes, as an automated part of my journal.&lt;/p&gt;
&lt;h3 id=&quot;capturing-tasks&quot;&gt;Capturing Tasks&lt;/h3&gt;
&lt;p&gt;Viewing and managing existing tasks is only half the picture. I want to be able to capture tasks when they come into my head so I don’t have to keep focusing on them as my day continues around me. My catch-all location for new tasks is in my daily note. Obsidian Tasks has a command shortcut for creating a new task, with a nice modal that lets me enter due dates similar to Todoist. As a lightweight replacement for &lt;a href=&quot;https://orgmode.org/manual/Tracking-your-habits.html&quot;&gt;org-habit&lt;/a&gt;, I keep repeating tasks in their own notes so I can track how often I complete them. I also will add tasks that relate to specific topics directly in-line to notes, relying on my agenda queries to slurp them up properly and link back to the context that I in which created them.&lt;/p&gt;
&lt;p&gt;When I’m on my computer or actively using Obsidian on mobile, this works great. But &lt;a href=&quot;{{&lt; relref &amp;#x22;digital-mindfulness.md&amp;#x22; &gt;}}&quot;&gt;I’m actively trying to spend less time idly on my devices&lt;/a&gt;. There’s lots of moments where I’ve realized I need to capture a task but I’m not actively using Obsidian. What then?&lt;/p&gt;
&lt;h3 id=&quot;siri-shortcuts&quot;&gt;Siri Shortcuts&lt;/h3&gt;
&lt;p&gt;I’ve never used Siri Shortcuts before, but &lt;a href=&quot;https://pawa.lt&quot;&gt;pawalt&lt;/a&gt; shared a shortcut with me that takes text input (or voice input from Siri) and appends it to a file in his vault. Here’s my slightly modified version:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/images/obsidian-shortcut-source.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;It works beyond perfectly in tandem with Obsidian Tasks, since I can add mobile tasks to the same note and they’ll still be collected with everything else in the agenda view. “Everything is a file”, so the Obsidian developers didn’t need to write any particular integrations with Shortcuts. The shortcut just edits a file on disk, and Obsidian reads the file as it normally would, interoperating seamlessly without even knowing it.&lt;/p&gt;
&lt;p&gt;This shortcut inspired me to go one step further. It’s a small hastle, but a hastle nonetheless to always have to navigate back to my Agenda note from another part of the app when I want to see my tasks on mobile. If I was using a dedicated to-do app like Reminders or Todoist, I would always be in the task-management view, since it’s the only thing those apps do.&lt;/p&gt;
&lt;p&gt;Obsidian has a pretty easy way to create bookmarks and shortcuts to notes from outside of the app through &lt;a href=&quot;https://help.obsidian.md/Advanced+topics/Using+obsidian+URI&quot;&gt;Obsidian URI support&lt;/a&gt;. And using the &lt;a href=&quot;https://github.com/Vinzent03/obsidian-advanced-uri&quot;&gt;Advanced URI plugin&lt;/a&gt;, I can create a bookmark that will always open my Agenda view in preview mode rather than edit mode. I’m sure there’s multiple ways to add bookmarks to the homescreen, but I just created a one-action Shortcut to open &lt;code&gt;obsidian://advanced-uri?filename=Agenda&amp;#x26;viewmode=preview&lt;/code&gt;, and iOS and Obsidian handle the rest. I have my two new shortcuts on my homescreen, and they look and work almost as seamlessly as if they were native apps.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/images/obsidian-shortcuts.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;These shortcuts were a real “holy shit” moment for me as I was building out my personal workflows. I certainly didn’t expect iOS, which is notoriously difficult to customize, to be able to support these kinds of custom actions and integrations with Obsidian. It’s opened my eyes to what Siri Shortcuts can do. iOS doesn’t let me edit the system’s crontab and set a Python script to run every four hours. However, Shortcuts &lt;em&gt;does&lt;/em&gt; let me create a “Personal Automation” to run a shortcut that I write every weekday at 8am, or every time I enter or leave a geofenced area. I’ll certainly be thinking more about what I can do with Shortcuts going forward.&lt;/p&gt;
&lt;h3 id=&quot;kanban-view&quot;&gt;Kanban View&lt;/h3&gt;
&lt;p&gt;I wanted to give an honorable mention to the &lt;a href=&quot;https://github.com/mgmeyers/obsidian-kanban&quot;&gt;Obsidian Kanban&lt;/a&gt; plugin. Trello never worked for me as a general task management system, but I’ve found that kanban boards do have their place in my workflow for personal project management. I’m currently working on a &lt;a href=&quot;https://github.com/davish/rogue-asteroids&quot;&gt;video game&lt;/a&gt; in my spare time. Having a board to keep track of my ideas has been a great way to organize my thoughts and the direction I want to take the game. I created another board to track article ideas through the writing process for this blog, too. And like everything in Obsidian, the plugin stores all the data as readable, plaintext Markdown files.&lt;/p&gt;
&lt;h3 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h3&gt;
&lt;p&gt;And that’s the first part of my Obsidian workflow! Even if none of this feels like it clicked for you personally, I hope you learned something about Shortcuts, automation, or a new Obsidian plugin. And I hope to catch you in the next part of the series!&lt;/p&gt;</description><pubDate>Sun, 23 Jan 2022 01:20:05 GMT</pubDate></item><item><title>Thinking with Obsidian</title><link>https://davi.sh/blog/2022/01/obsidian-zero/</link><guid isPermaLink="true">https://davi.sh/blog/2022/01/obsidian-zero/</guid><description>&lt;p&gt;This is the first part of an in-progress series on the &lt;a href=&quot;https://obsidian.md&quot;&gt;Obsidian knowledge base&lt;/a&gt;. You can find all the articles with the &lt;a href=&quot;/tags/obsidian&quot;&gt;obsidian tag&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I’m not an organized person by nature. I tend to try to just commit important things to memory rather than write them down in any systematized way, which means I end up forgetting things about 20-30% of the time, and just hoping I remembered the most important things in some kind of implicit neural priority queue. I’ve tried a bunch of different &lt;a href=&quot;https://todoist.com/app/&quot;&gt;task management&lt;/a&gt; and &lt;a href=&quot;https://bulletjournal.com&quot;&gt;notetaking systems&lt;/a&gt; before, but nothing really stuck. At the end of the day it’s because I always struggled to organize my thoughts in a single hierarchy. Even a regular paper notebook is organized implicitly in a timeline, with older notes in the front and newer notes in the back. I always spend way more time thinking about where my note should fit in the hierarchy than I do actually capturing my thoughts.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;The closest I got to a system that worked for me was &lt;a href=&quot;https://orgmode.org&quot;&gt;Org Mode&lt;/a&gt; in &lt;a href=&quot;/blog/2020/03/switching-to-emacs/&quot;&gt;Emacs&lt;/a&gt;. Org is a full notetaking and outlining system first and foremost, but &lt;a href=&quot;https://orgmode.org/manual/Agenda-Views.html&quot;&gt;Org’s agenda views&lt;/a&gt; were game-changing for me: I could write down tasks and events in &lt;em&gt;any&lt;/em&gt; file, and my agenda view would aggregate and sort them all to show me what was important. The issue of up-front organization completely went away. Org was an extremely powerful system, but its only true implementation was the major mode written in Emacs LISP for the Emacs environment. Mobile apps exist that can parse and manipulate subsets of Org’s syntax,&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; but none of them handle all the features of Org I used, like recurring tasks and agenda view, as well as the original implementation. I loved Org for how easy it was to capture thoughts and tasks, but I still couldn’t effectively capture and process notes and tasks when I wasn’t in front of my own computer with Emacs open. Unfortunately, that was pretty limiting for me.&lt;/p&gt;
&lt;p&gt;So I was open to alternatives. Personal Knowledge Management (PKM) has been a trend for a few years now, popularized by &lt;a href=&quot;https://roamresearch.com&quot;&gt;Roam&lt;/a&gt; and its interconnected web of small, atomic notes. Like Org Mode, there was no need for a strict hierarchy — the relationship between notes is implicit through bidirectional, internal links. There’s a lot of choices now in this space. Roam Research, &lt;a href=&quot;https://notion.so&quot;&gt;Notion&lt;/a&gt;, &lt;a href=&quot;https://www.orgroam.com&quot;&gt;Org-roam&lt;/a&gt;, &lt;a href=&quot;https://www.dendron.so&quot;&gt;Dendron&lt;/a&gt;, &lt;a href=&quot;https://logseq.com&quot;&gt;Logseq&lt;/a&gt;, &lt;a href=&quot;https://obsidian.md&quot;&gt;Obsidian&lt;/a&gt; all come to mind. If I really had to be honest with myself, I chose Obsidian primarily because it was the only option besides Emacs with Org-roam that included &lt;code&gt;vi&lt;/code&gt; keybindings to edit text. In addition, its feature-equal mobile app was an attractive improvement over my existing system.&lt;/p&gt;
&lt;p&gt;While I chose Obsidian relatively superficially, I’ve been really happy with the product for two main reasons: extendability and interoperability.&lt;/p&gt;
&lt;p&gt;Obsidian follows VSCode’s philosophy of having a rock-solid, minimalist core with most functionality built on top as plugins.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;a href=&quot;https://help.obsidian.md/Obsidian/Obsidian#What+is+Obsidian&quot;&gt;The Obsidian docs&lt;/a&gt; say that “Used in the most basic way, you can edit and preview Markdown files” within Obsidian. The one real addition to that is that internal links are “first class citizens,” and Obsidian’s core will index your internal links for autocomplete and note renaming. Almost everything else in Obsidian, from the file manager to the graph view to the word count in the text editor, is a plugin built on top of the base Markdown text editor. The fact that the developers have written so many core plugins using the plugin API (&lt;strong&gt;&lt;em&gt;27&lt;/em&gt;&lt;/strong&gt;, by my count) means that they’ve spent a lot of time thinking about the ergonomics of that API, and the quality of &lt;a href=&quot;https://obsidian.md/plugins&quot;&gt;Obsidian’s third-party plugin ecosystem&lt;/a&gt; shows how this pays off. For a relatively new app, there’s almost a plugin for anything, from &lt;a href=&quot;https://github.com/elias-sundqvist/obsidian-annotator&quot;&gt;PDF annotation&lt;/a&gt; to an &lt;a href=&quot;https://github.com/joethei/obsidian-rss&quot;&gt;RSS reader&lt;/a&gt; fully inside the editor. And if what you’re looking for doesn’t exist, it’s possible to make it yourself, either as a standalone plugin or with some of Obsidian’s advanced scripting capabilities that some plugins expose.&lt;/p&gt;
&lt;p&gt;Obsidian is fully based on local Markdown files. While there’s some non-standard syntax for internal links, its emphasis on such a universal format makes it extremely easy for plugins to work together and for Obsidian to interop with external tools. All plugins read and write to Markdown files. If you want to sync your Obsidian vault with some third-party service, just write a script to pull down data from an API and write it to Markdown files. It’ll show up in your Vault when you open it next. It’s that easy! I’ve been able to do it to great success in my own setup.&lt;/p&gt;
&lt;h3 id=&quot;my-obsidian-system&quot;&gt;My Obsidian System&lt;/h3&gt;
&lt;p&gt;I’m pretty happy with the org-mode-inspired Obsidian workflow that I’ve built. Over the next week or two I’m planning on posting a series that goes into detail on parts of my workflow, explaining plugins and configuration as I go. These sorts of posts were really helpful to me when setting up my org-mode workflows originally, and I’m hoping to contribute to a similar ecosystem of workflows for Obsidian. Luckily, Obsidian is modular enough that you can grab only the parts that are exciting for yourself. I’ll see you in the next post!&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://beorgapp.com&quot;&gt;BeOrg&lt;/a&gt; and &lt;a href=&quot;https://plainorg.com&quot;&gt;Plain Org&lt;/a&gt; are two examples of polished apps which focus on different subsets of org-mode’s syntax. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;I’d be remiss not to note that this itself is an evolution of Emacs’s now 40-year-old configuration-over-convention philosophy that leads some to refer to Emacs more as an “editor toolkit”, or joke that Emacs is “a great operating system that just needs a good text editor.” (&lt;a href=&quot;https://news.ycombinator.com/item?id=7978048&quot;&gt;HN source&lt;/a&gt;, but you can find this sentiment all over.) &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><pubDate>Fri, 21 Jan 2022 04:00:34 GMT</pubDate></item><item><title>Digital Mindfulness</title><link>https://davi.sh/blog/2022/01/digital-mindfulness/</link><guid isPermaLink="true">https://davi.sh/blog/2022/01/digital-mindfulness/</guid><description>&lt;p&gt;I’m not normally one for new year’s resolutions. Mostly because I’ve found it hard to make lasting changes to my habits in the past. This year, though, I’ve got a pretty open-ended one: to be more mindful and intentional about how I use digital technology and the Internet.&lt;!--more--&gt;&lt;/p&gt;
&lt;h3 id=&quot;digital-minimalism&quot;&gt;Digital Minimalism&lt;/h3&gt;
&lt;p&gt;I read &lt;a href=&quot;https://www.calnewport.com/books/digital-minimalism/&quot;&gt;&lt;em&gt;Digital Minimalism&lt;/em&gt; by Cal Newport&lt;/a&gt; over the summer, but the signifance behind the book didn’t sink in for me until the end of 2021.&lt;/p&gt;
&lt;p&gt;The main prescription in &lt;em&gt;Digital Minimalism&lt;/em&gt; is the “digital declutter”: thirty days during which you unplug from &lt;em&gt;all&lt;/em&gt; digital technology not directly related your job. During and after the declutter, you’re supposedly able to more clearly see what kinds of technology are truly beneficial to your everyday life, and what you can live without. Originally I thought this was overly onerous. I thought that if the declutter was how you &lt;em&gt;determined&lt;/em&gt; which activities were “critical”, then how could you set aside a full month when some of those activities &lt;em&gt;were&lt;/em&gt; critical?&lt;/p&gt;
&lt;p&gt;You might have noticed that I inserted the word “critical” into my interpretation of the declutter. I’ve been conditioned, or maybe I’ve conditioned myself, to think about a lot of my own habits as critical, when really they just facilitate a nice, local maximum of contentedness. It only took a few days’ declutter for me to begin to see beyond the rut, though.&lt;/p&gt;
&lt;h3 id=&quot;taking-time-off&quot;&gt;Taking time off&lt;/h3&gt;
&lt;p&gt;In the last weeks of 2021, I took my first real vacation days since I started working at MongoDB in August. I was home with my parents, away from the busyness of New York City and without any pressing commitments. Even just not working was more of a shock than I’d felt during inter-semester breaks back in school — after two days off, I had a bit of a &lt;a href=&quot;https://giphy.com/gifs/ftXmTBgSnsJ8c&quot;&gt;“I’m not sure what to do with my hands”&lt;/a&gt; feeling. That’s probably because I was generally burned out by the end of every semester-long sprint in college. Working life for me has been much more of a maintainable marathon, so I was able to recharge past a simple baseline over this break.&lt;/p&gt;
&lt;p&gt;I decided to go a step further than disconnect from work and not even go on my personal laptop for a few days. I spent most of my time reading rather than watching TV, which I also tried to stay away from. I went on walks without listening to podcasts or music, and generally just let myself relax a muscle that I didn’t even know was under tension.&lt;/p&gt;
&lt;h3 id=&quot;stepping-off-the-treadmill&quot;&gt;Stepping off the treadmill&lt;/h3&gt;
&lt;p&gt;Steve Jobs once called the personal computer &lt;a href=&quot;https://www.youtube.com/watch?v=KmuP8gsgWb8&quot;&gt;“a bicycle for the mind”&lt;/a&gt;. He meant it in the context of how human-made tools can drastically increase our effeciency. The quote also brings to mind how a computer can be both fun and stimulating, like a bicycle. I’ve realized that modern digital technology and the internet gives even more layers of meaning to the metaphor.&lt;/p&gt;
&lt;p&gt;Like riding a bike, using digital technology takes energy and focus. It’s clear why this is true on a mechanical level. Most of my personal media consumption is in the form of reading articles and comment threads. Focusing on text on a backlit screen requires a level of focus and concentration that I didn’t realize I was expending until I wasn’t doing it anymore.&lt;/p&gt;
&lt;p&gt;Being “wired in” also causes other mental muscles to atrophy. Even two days into my declutter, I found myself free associating and daydreaming when I didn’t have anything else occupying my attention in a way that I hadn’t done for years.&lt;/p&gt;
&lt;p&gt;When I’m at a computer or on my phone, I feel like I’m in a constant feedback loop with the machine. I perform some action, get some response, which might prompt some hit of dopamine, and regardless will most likely perform another action (or the same one again). The presence of a feedback loop itself wasn’t troubling — all of consciousness involves feedback loops. When I stepped off that machine-human-machine treadmill, the new feedback loop was completely internal to my mind. I was able to think much more freely and with less friction, because I was no longer beholden to the rigid limits of how the computer logically responds to my actions.&lt;/p&gt;
&lt;h3 id=&quot;fidget-spinners&quot;&gt;Fidget spinners&lt;/h3&gt;
&lt;p&gt;I started using &lt;a href=&quot;https://www.opal.so&quot;&gt;Opal&lt;/a&gt; to physically block traffic to distracting websites, like Twitter, YouTube and Hacker News on my phone, and edited &lt;code&gt;/etc/hosts&lt;/code&gt; on my laptop to achieve the same effect. I noticed that any time I had any spare moment, I felt like I needed to check on something on my phone. Lots of times it would be completely subconscious. I wouldn’t even realize that I’d unlocked my phone and gone to HN or &lt;a href=&quot;https://lobste.rs&quot;&gt;lobste.rs&lt;/a&gt; until the blank page on Safari snapped me back into reality.&lt;/p&gt;
&lt;p&gt;Given a free moment, I just wasn’t letting my mind wander. I was subconsciously craving that feedback-loop mental treadmill and the small chance for a hit of dopamine if there happened to be a new video or story or comment or tweet or whatever – and there usually wasn’t. I became frustrated that my phone felt &lt;em&gt;useless&lt;/em&gt; with this new limitation. What was an iPhone good for if it couldn’t save me from being bored?&lt;/p&gt;
&lt;p&gt;Slowly, I started stepping back and realizing that that’s a pretty idiotic thought. Smartphones can look up any fact imaginable on Wikipedia, show you how to drive anywhere you want with GPS, or order a taxi for you with Uber and Lyft if you can’t drive one yourself. As much as I used my phone as a mental fidget spinner, it was still a real tool that had real uses. More disturbingly, I clearly got along just fine before I got on social media and started using a smartphone in 2013. Why did I not feel fine now?&lt;/p&gt;
&lt;h3 id=&quot;what-changed&quot;&gt;What changed?&lt;/h3&gt;
&lt;p&gt;What’s interesting about all of this is that I’d already cut out virtually all algorithmic social media (Instagram, Twitter, Facebook) years ago. My main muscle memory on a new tab takes me to news.ycombinator.com, which has a much gentler algorithm than most main-stream social media, no infinite scroll, and no ads.&lt;/p&gt;
&lt;p&gt;I think an important mental shift for me happened at the start of COVID-19 lockdowns in March 2020. I felt like I had nowhere to be, and nothing to do, and so &lt;em&gt;why not&lt;/em&gt; just sit in front of my computer, refreshing message boards and (doom-)scrolling through Twitter? Once formed, habits are hard to break. As lockdowns eased up, the country reopened, and life started to resemble the Before Times™ more and more, that muscle memory that I’d formed in the first half of 2020 didn’t go away on its own. It took me another 18 months for me to put a name to the feelings and identify what had really changed. As the US deals with another variant wave in Omicron, I want to make sure I don’t entrench the same habits even more.&lt;/p&gt;
&lt;p&gt;One of the things I learned from &lt;em&gt;Digital Minimalism&lt;/em&gt; is that “minimalism” as a philosophy doesn’t just mean “less is more”. It’s a recognition that some things in life &lt;em&gt;are&lt;/em&gt; zero-sum and that not every output has a linear relationship with its inputs. I can keep increasing my short-term contentedness by spending more time online, albeit with quickly diminishing returns. The global maximum for my own happiness levels might, and probably does, lie in dialing down my time spent staring at screens to a much lower level than what I’ve been living with day-to-day over the past few years.&lt;/p&gt;
&lt;h3 id=&quot;where-to-go-from-here&quot;&gt;Where to go from here&lt;/h3&gt;
&lt;p&gt;A big goal for me is to reframe &lt;em&gt;why&lt;/em&gt; I pick up my phone (and laptop) in the first place. Before this break, my primary use of technology was to &lt;strong&gt;stave off boredom&lt;/strong&gt;. Even if I got work done on a personal project or caught up with friends, the vast majority of “pickups” were mental fidgets because I couldn’t find any other external stimuli at that precise moment in time.&lt;/p&gt;
&lt;p&gt;Going forward I want to shift my objective to be to use technology to &lt;strong&gt;accomplish a goal&lt;/strong&gt;. When I pick up my phone or laptop, I should already have a specific intention in mind beyond “keeping myself occupied”. Something like “I have an idea for a blog post and want to start writing” or “I wonder how so-and-so is doing? I should call them.”&lt;/p&gt;
&lt;p&gt;Blocking habit-laden websites will help re-train my muscle memory as I go, but I also need to be conscious and mindful about my behavior. When I find myself with a free moment, I should just try and let my mind wander, and be content with &lt;em&gt;internal&lt;/em&gt; stimuli. If I’m really craving external stimulation, I should pick up something long-form, like a book, or open a weekly newsletter I’ve subscribed to.&lt;/p&gt;
&lt;p&gt;My digital declutter was much shorter than what Newport recommends in &lt;em&gt;Digital Minimalism&lt;/em&gt;, but I was able to see the power in the approach. I would need to balance a comprehensive declutter with wanting to remain social as a young person whose friends wouldn’t also be doing a declutter. It’ll probably involve setting up auto-responses somehow and making sure friends know to call rather than text me. A few weeks ago I would’ve considered a month-long declutter completely out of the question, but now I might consider it down the line.&lt;/p&gt;</description><pubDate>Tue, 04 Jan 2022 22:19:42 GMT</pubDate></item><item><title>PiClock: Waking Up to the Unix Philosophy</title><link>https://davi.sh/blog/2021/05/pi-clock/</link><guid isPermaLink="true">https://davi.sh/blog/2021/05/pi-clock/</guid><description>&lt;p&gt;I built a clock! &lt;a href=&quot;https://github.com/davish/pi-clock&quot;&gt;It runs Linux&lt;/a&gt;.
Sounds like quite the over-engineering project, but I learned a ton
along the way and was able to flex some muscles that had been lying
dormant for a while. I write a decent amount of code, but I hadn’t
worked on an electronics project in years and had never really embarked
on a 3D modeling or design project outside the classroom.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/images/piclock/clock-activated.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I discovered &lt;a href=&quot;https://hackaday.com/2021/04/25/keep-in-touch-with-grandma-with-this-lo-tech-interface/&quot;&gt;the
Yayagram&lt;/a&gt;
a few weeks ago, and my project was inspired by many of the same
principles. Although I’m building this clock for myself rather than an
older family member, I love the idea that objects can be smart and
internet-connected in useful ways without interactive touchscreens.
Making smart devices with lower-fidelity input and output can help slow
down the feedback loops that we often encounter with the modern
internet. I’m hoping my clock can be a step for me away from
doomscrolling and towards a more measured relationship with content
consumption on the internet.&lt;/p&gt;
&lt;h1 id=&quot;the-problem&quot;&gt;The Problem&lt;/h1&gt;
&lt;p&gt;I’m a relatively deep sleeper. I sometimes sleep through alarms, and
even when I wake up most of the time, I fall asleep again just as
quickly after I hit “off” or “snooze.” I’ve resolved to just
setting four or five alarms staggered five minutes apart around the time
I want to wake up. It’s not a use-case that’s supported too well by
Apple’s Clock app:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/images/piclock/clock.png&quot; alt=&quot;Lots of alarms to scroll through and old TODOs that I&amp;#x27;ve left for
myself in the alarm names.&quot;&gt;&lt;/p&gt;
&lt;p&gt;On top of all this, needing to grab my phone to snooze my alarm makes it
easy for me to keep scrolling rather than get up and start my day.
Off-the-shelf digital alarm clocks are a lot less distracting than the
general-purpose internet communicators we carry around all day. Still,
they generally can’t set more than one or two alarms and are difficult
to program when schedules change day-to-day.&lt;/p&gt;
&lt;h1 id=&quot;the-spec&quot;&gt;The Spec&lt;/h1&gt;
&lt;p&gt;I wanted a clock that would:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Let me set multiple alarms per wake-up.&lt;/li&gt;
&lt;li&gt;Not be distracting in the morning when I want to start my routine.&lt;/li&gt;
&lt;li&gt;Provide a bit of news to begin my day without sucking me into an
algorithm-powered bottomless doom vortex.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Point #3 means that this clock needed to be internet-connected. But
point #2 means that it couldn’t be distracting. This rules out writing
a custom app for my phone or something like an Echo Show.&lt;/p&gt;
&lt;h1 id=&quot;the-hardware&quot;&gt;The Hardware&lt;/h1&gt;
&lt;h2 id=&quot;electronics&quot;&gt;Electronics&lt;/h2&gt;
&lt;p&gt;My strange alarm staggering requirements meant that I also felt like
I’d want to program in custom behavior. With the interaction of all
these requirements, I decided to have the brains of the clock be a
Raspberry Pi Zero W running a standard Raspbian distribution. For the
splash of news in requirement #3, I wanted to have the choice between
audio podcasts and small snippets from Twitter and newsletters, along
with the weather and recent COVID statistics for my area, so I opted to
add in a thermal receipt printer for a more low-screen digital output.&lt;/p&gt;
&lt;p&gt;I sourced all the parts from &lt;a href=&quot;https://www.adafruit.com&quot;&gt;Adafruit&lt;/a&gt;, which
has a fantastic selection and quick delivery times from their warehouse
in NYC to where I’ve been in Philadelphia. I’ve listed a more-or-less
complete &lt;a href=&quot;https://github.com/davish/pi-clock/blob/main/bom.org&quot;&gt;bill of
materials&lt;/a&gt; in the
Github repository.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/images/piclock/components.jpg&quot; alt=&quot;All the electronics &amp;#x22;exploded&amp;#x22; out of the
enclosure.&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;enclosure&quot;&gt;Enclosure&lt;/h2&gt;
&lt;p&gt;The enclosure was a challenge — my first non-school project in
SolidWorks since taking a mechanical design elective two years ago and
my first 3D printing project since I was on my high school’s robotics
team. If I’d had access to a laser cutter, then that would’ve been my
go-to since the enclosure is just a simple box. Engineering facilities
at Penn were closed down to most undergrads this year without pressing
work to do, so my trusty Printrbot Simple Metal would have to cut it.&lt;/p&gt;
&lt;p&gt;I had more issues with bed adhesion and warping than I’d ever
encountered before. Once I ironed out the adhesion issues, I couldn’t
pull the finished prints off the bed without applying some serious lever
force. I ended up investing in a &lt;a href=&quot;https://www.buildtak.com/product/buildtak-flexplate-system/&quot;&gt;BuildTak
FlexPlate&lt;/a&gt;
to avoid damaging the printer more going forward.&lt;/p&gt;
&lt;p&gt;I knew that the software was the most exciting part of this for me and
what I was most comfortable with. So I didn’t let myself get started on
the coding until after I’d finish the enclosure; otherwise, I’d be
left with a mess of wires on my bedside table until the end of time.
Ultimately, though, I’m happy with the finished result. It’s about as
small as possible while still fitting all the components and looks clean
by my bed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/images/piclock/enclosure.jpg&quot; alt=&quot;Components packed into the enclosure before the top goes
on.&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;the-software&quot;&gt;The Software&lt;/h1&gt;
&lt;p&gt;Adafruit’s whole library stack for their peripherals, I’m pretty sure,
is only available for Python on the Pi and Arduino for more basic
microcontrollers. This basically made my programming language decision
for me, and I’m more than comfortable enough in Python to be happy with
that solution.&lt;/p&gt;
&lt;p&gt;Before this, my other electronics/embedded computing experience has come
from from Arduino, with its &lt;code&gt;setup()&lt;/code&gt;{.verbatim} and &lt;code&gt;loop()&lt;/code&gt;{.verbatim}
functions that control the whole project. I was dreading writing all my
code in a single Python &lt;code&gt;while True&lt;/code&gt;{.verbatim} loop that would get
initiated on system startup. When I worked on a &lt;a href=&quot;https://hackaday.io/project/3627-trinket-watch&quot;&gt;similar digital watch
project&lt;/a&gt; five years ago,
the code ended up being extremely brittle, with different modes and
state riddled all over the place. Fundamentally, I realized I was asking
myself why I would try and write a robust mini-OS for a clock when the
computer it’s running on is already running Linux?&lt;/p&gt;
&lt;p&gt;The architecture I landed on is spread among different files and uses
&lt;code&gt;cron&lt;/code&gt;{.verbatim} to orchestrate all the behavior. Python scripts
control various aspects of the clock’s functions and are called on
one-minute intervals. Luckily, my display doesn’t have space for
seconds since this is the smallest interval available for cron.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/davish/pi-clock/blob/main/tick.py&quot;&gt;&lt;code&gt;tick.py&lt;/code&gt;{.verbatim}&lt;/a&gt;:
updates the seven-segment displays with the current time.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/davish/pi-clock/blob/main/alarm.py&quot;&gt;&lt;code&gt;alarm.py&lt;/code&gt;{.verbatim}&lt;/a&gt;:
checks alarm settings and rings an alarm if appropriate.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/davish/pi-clock/blob/main/podcast.py&quot;&gt;&lt;code&gt;podcast.py&lt;/code&gt;{.verbatim}&lt;/a&gt;:
depending on the command line argument, downloads a new podcast
episode from an RSS feed or plays the most recent one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s been my first real foray into building utilities that faithfully
follow the Unix philosophy of doing one small thing well in a composable
way. Settings like alarm schedules are saved in JSON files, and I’m
planning on writing wrapper commands around
&lt;a href=&quot;https://stedolan.github.io/jq/&quot;&gt;&lt;code&gt;jq&lt;/code&gt;{.verbatim}&lt;/a&gt; to make it easy to
modify settings without dropping into an editor over SSH. It’s
certainly been discussed how composable Unix utilities can lend
themselves well to a productive developer workflow. It was really cool
seeing this approach work well for an embedded system, too, albeit
without actual realtime requirements.&lt;/p&gt;
&lt;h1 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h1&gt;
&lt;p&gt;There are certainly some more directions I want to take the clock in. On
the software side, I still haven’t played around with the receipt
printer much. It’ll be interesting to experiment with data sources and
layouts that can work well for morning updates. I also think it could be
cool to print out transit directions on-demand. I’m moving to New York
City soon, and I always find myself making sure I don’t close out of
Google Maps when I’m on the subway, since service can be spotty and I
often forget what stop I need to get off on or what train to transfer to
next. I’m also thinking about integrating to-dos for the day to track
what I plan on getting done in the morning.&lt;/p&gt;
&lt;p&gt;On the hardware end, there’s certainly room for expansion. The clock
currently has a single pushbutton that I use to interact with alarms. It
might be cool to add a
&lt;a href=&quot;https://www.adafruit.com/product/3317&quot;&gt;time-of-flight&lt;/a&gt; sensor for
gesture control. Or maybe a &lt;a href=&quot;https://www.adafruit.com/product/4750&quot;&gt;fingerprint
sensor&lt;/a&gt; to allow for different
controls based on what finger I press to the sensor.&lt;/p&gt;
&lt;p&gt;Code exists in any number of copies. It’s been awesome tinkering and
tweaking a physical &lt;em&gt;thing&lt;/em&gt; that’s more-or-less one of a kind. And
unlike an off-the-shelf product, it’ll be cool to see it evolve over
time as I tweak whaat I’ve got add more features to it.&lt;/p&gt;</description><pubDate>Thu, 20 May 2021 17:00:00 GMT</pubDate></item><item><title>Top Classes in Undergrad</title><link>https://davi.sh/blog/2021/04/top-classes/</link><guid isPermaLink="true">https://davi.sh/blog/2021/04/top-classes/</guid><description>&lt;p&gt;I’m graduating from college in May. Over my four years working towards
my degree, I’ve taken about forty different courses. As I enter the
last month of school, I thought it would be a good time to look back and
reflect on the courses that I enjoyed the most. I’ve picked out five
classes, and instead of trying to make any absolute ranking of them, I
just decided to present them in the order they were taken, and try and
express how each one impacted me.&lt;/p&gt;
&lt;h1 id=&quot;cis-240-introduction-to-computer-architecture-fall-2018&quot;&gt;CIS 240: Introduction to Computer Architecture (Fall 2018)&lt;/h1&gt;
&lt;p&gt;CIS 240 with &lt;a href=&quot;https://www.cis.upenn.edu/~cjtaylor/&quot;&gt;C.J. Taylor&lt;/a&gt; was my
favorite introductory CS class I took at Penn. I came into school with
some programming experience, but I was certainly ignorant of the inner
workings of &lt;em&gt;how&lt;/em&gt; computers took the code I would write, execute it and
ultimately give output back to me. 240 started with transistors and
logic gates, built up to CMOS circuitry, before jumping the boundary
between atoms and bits and introducing us to machine code, assembly, and
C for an educational architecture known as
&lt;a href=&quot;https://www.cis.upenn.edu/~cis371/17sp/lc4.html&quot;&gt;LC4&lt;/a&gt;. I’ve heard that
pointers in C are a notoriously difficult concept for beginners to
grasp, but the evolution up the ladder of abstraction within a computer
made C feel like a natural extension of assembly. The course had a very
intentional flow to it, and every separate concept felt like it fit into
the course well.&lt;/p&gt;
&lt;p&gt;Outside of the material, Professor Taylor brought great enthusiasm and a
sense of humor to the class. Along with an arsenal of cheesy jokes (my
favorite kind) that I’m sure were honed over the semesters lecturing
for the course, he came to our midterm exam, which happened to be during
the final block of class on the afternoon of Halloween, dressed as Darth
Vader, mask and all. He somehow managed to stay in character the entire
exam, which I’ve always respected.&lt;/p&gt;
&lt;h1 id=&quot;psci-232--comm-226-introduction-to-political-communication-fall-2018&quot;&gt;PSCI 232 / COMM 226: Introduction to Political Communication (Fall 2018)&lt;/h1&gt;
&lt;p&gt;This course was billed to me as one of the most difficult, but also most
rewarding, political science courses at Penn. &lt;a href=&quot;https://en.wikipedia.org/wiki/Kathleen_Hall_Jamieson&quot;&gt;Professor Kathleen Hall
Jamieson&lt;/a&gt; had been
teaching the class since the late 90s, and her expertise shone through.
232 was all about presidential elections, the media, and the electorate.
It took a deep dive into presidential campaign advertising in every
presidential campaign since Eisenhower’s election in 1952. Each week
we’d watch campaign ads from a certain campaign, and Professor Jamieson
would dig into what made each one persuasive to its audience, or why a
given ad flopped.&lt;/p&gt;
&lt;p&gt;More important to me than the technicalities of advertising and public
communication, we dug into the role that the media itself has in shaping
the narrative around elections, and how campaigns can rise or fall in
how they take advantage of the media’s agenda-setting powers. Jamieson
has been an outspoken voice about deception and falsehoods in politics
&lt;a href=&quot;https://www.washingtonpost.com/archive/opinions/1988/10/30/our-appalling-politics/fefb1d63-1570-4875-872b-27e2947d38df/&quot;&gt;for
decades&lt;/a&gt;.
Standing in 2021, her warnings back then certainly seemed prescient, and
the class helped teach me the very active role that social and
traditional media play in helping decide how our politics unfolds in the
United States.&lt;/p&gt;
&lt;h1 id=&quot;psci-253-international-politics-of-the-middle-east-fall-2019&quot;&gt;PSCI 253: International Politics of the Middle East (Fall 2019)&lt;/h1&gt;
&lt;p&gt;The syllabus was so daunting I almost dropped 253 before the first
lecture. Six books in fourteen weeks would add up to roughly two hundred
pages of reading each week, and on top of an upperclassman engineering
course load, it didn’t seem particularly feasible. But after a single
lecture with &lt;a href=&quot;https://live-sas-www-polisci.pantheon.sas.upenn.edu/people/standing-faculty/robert-vitalis&quot;&gt;Robert
Vitalis&lt;/a&gt;,
I knew I would stick it through. I feel like it’s pretty easy to
determine which professors have enjoyed tenure for a good amount of
time. While the observation is generally considered to be a nock against
a professor’s dedication to their teaching responsibilities, I’ve come
to think that there are two distinct sub-types of the “tenured
professor” stereotype: there’s the stereotypical apathetic professor,
but there’s also those who take risks with their pedagogy that let you
know that they haven’t had to justify their teaching methods to a
committee for some time. Professor Vitalis brought a particularly wacky
attitude to his lectures, often starting class with comments and
observations that were intentionally provocative to stir up discussion
and disagreement among the class. Even the 200-page weekly readings
were, in fact, a teachable moment. Our TA let it slip that Dr.
Vitalis’s goal was to teach us how to skim effectively in a sort of
“baptism by fire.”&lt;/p&gt;
&lt;p&gt;The books we read in the course also tied into each other in
thought-provoking ways. Each dealt with American intervention in the
Middle East over the 20th century in a different light. Written
assignments were comparative book reviews, a format I haven’t heard any
other professor using in their classes. The fundamental question of the
course, it seemed, was &lt;em&gt;why&lt;/em&gt;? Why did the US maintain its interventions
on the other side of the world? Why did it present those interventions
to the public as it did? We never reached a real answer to any of these
questions, but the books brought another lens through which to look at
government and the responsibility of those that hold the levers of power
in American society, over not just our lives, but the lives of people
all over the world.&lt;/p&gt;
&lt;h1 id=&quot;cis-341-compilers-and-interpreters-spring-2020&quot;&gt;&lt;a href=&quot;https://www.seas.upenn.edu/~cis341/current/&quot;&gt;CIS 341&lt;/a&gt;: Compilers and Interpreters (Spring 2020)&lt;/h1&gt;
&lt;p&gt;I certainly didn’t expect 341 to be the class that perfectly capped off
my computer science education when I first signed up for it. The course
is taught in a bottom-up manner, starting with an assembler for a subset
of X86 before moving up to an LLVM to x86 compiler backend. Only after
understanding some of the nuances of LLVM does the class move to the
frontend of the compiler and deal with lexing, parsing, and types.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cis.upenn.edu/~stevez/&quot;&gt;Steve Zdancewic&lt;/a&gt; expertly connected
all the disparate threads of computer science that I’d experienced
throughout college and showed how mathematical theory and engineering
discipline can be combined to enable the programming languages that
developers take for granted today. I don’t think there’s a better way
to explain this than by listing all the topics that 341 touched on, and
the other courses that it built on top of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CIS320 (Data Structures and Algorithms): Graph coloring for register
allocation&lt;/li&gt;
&lt;li&gt;CIS262 (Automata, Computability and Complexity): Parsing classes and
context-free grammars&lt;/li&gt;
&lt;li&gt;CIS240: Assembly language and machine code&lt;/li&gt;
&lt;li&gt;CIS371 (Computer Architecture II): Optimizations around instruction
ordering and processor pipelining&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I was able to put some knowledge from this course to use right away.
Penn Course Review needs to load in a SQL dump of new course reviews
every semester. Up until now, we’d relied on loading all the data into
a blank MySQL database that we spun up, and then querying it back out in
the format we expected. It was a lot of moving parts and generally
pretty slow, too. I was able to &lt;a href=&quot;https://github.com/pennlabs/penn-courses/blob/1b6bd0cdf3bafd6d590d4c5a767372631bf5ea9c/backend/review/import_utils/parse_sql.py#L30&quot;&gt;write a
parser&lt;/a&gt;
that pulled out that same data without having to run it through a full
MySQL instance. It just goes to show how the skills that are involved in
writing a compiler are useful in their own merits, in addition to being
used for compilers.&lt;/p&gt;
&lt;p&gt;341 was also where I got exposed to type theory as a subject for the
first time, and it sparked my interest in the study of programming
languages.&lt;/p&gt;
&lt;h1 id=&quot;cis-552-advanced-programming-fall-2020&quot;&gt;&lt;a href=&quot;https://www.seas.upenn.edu/~cis552/current/&quot;&gt;CIS 552&lt;/a&gt;: Advanced Programming (Fall 2020)&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cis.upenn.edu/~sweirich/&quot;&gt;Stephanie Weirich&lt;/a&gt; bills CIS552
as a class that “take[s] &lt;em&gt;good&lt;/em&gt; programmers and turn[s] them into
&lt;em&gt;excellent&lt;/em&gt; ones.” It’s Penn’s only full-credit class taught with
Haskell and going into it, I knew next to nothing about the language
besides that monads are something people find scary. This might be kind
of cliché, but learning Haskell through 552 did change how I thought
about programming across the board. Haskell evaluates expressions lazily
when they’re needed, and not when they’re defined. This is an oddity
in mainstream languages, but it makes you think more critically about
when your code runs. It helped me realize that some expressions in my
Python code were being evaluated at their definition when they should
have been evaluated lazily.&lt;/p&gt;
&lt;p&gt;552 shines in how it introduces monads and explains their usage. Monads
aren’t not something that clicks for everyone, and they certainly
didn’t for me right away. We started from the assumptioon that the
general definition of monads is too abstract for programming, and the
class worked more by example. Through the &lt;code&gt;Maybe&lt;/code&gt;{.verbatim} monad,
State monad, List monad, and a few other examples, the intuition slowly
built up for me about how powerful the concept can really be. Haskell’s
a fundamentally pure functional language that deals only with inputs and
outputs to functions. What monads add in this context is a way to
abstract out the glue code and plumbing behind a lot of programming
patterns in a way that makes code easier to follow and allows Haskell,
with a bit of syntactic sugar that the &lt;code&gt;do&lt;/code&gt;{.verbatim} block provides,
to take on some imperative-seeming features that one may find in a more
traditional language, like exceptions and global contexts, while
maintaining its underlying purity.&lt;/p&gt;
&lt;p&gt;The back half of the class was a great tour of what you can do when
strong typing is taken to its logical conclusion. I got to work with a
friend on a awesome final project, building a &lt;a href=&quot;https://github.com/eyingxuan/mqlint&quot;&gt;typechecker for MongoDB
aggregation queries&lt;/a&gt;. We were both
impressed with the final state of the project, but it’s pretty crazy
how much type-driven programming helped us out here. We started by
defining our abstract syntax tree, and then split up to work on the
schema and query parsers and the typechecker itself. Any changes we made
in the types were checked by the compiler and we were able to modify our
logic, and our monad stack, to add new features without worrying about
breaking existing ones.&lt;/p&gt;
&lt;p&gt;I don’t harbor any notions about how difficult it would be to find a
job working with Haskell in industry, but it’s certainly a language
that I’ll continue to play around with going forward. It’s got some
awesome ideas that are starting to percolate down towards more
mainstream languages like Rust.&lt;/p&gt;
&lt;h1 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h1&gt;
&lt;p&gt;All in all, I consider myself lucky to be able to study things that I
find interesting in their own right. Penn’s been difficult at times,
but it’s also afforded me the opportunity to learn from great
professors, work on &lt;a href=&quot;https://pennlabs.org&quot;&gt;super awesome projects&lt;/a&gt;, and
meet other students who are extremely driven and passionate about every
topic under the sun.&lt;/p&gt;
&lt;p&gt;I don’t know if there’s a single thread that runs through all five of
these classes. If I had to pick something, they all certainly had an
intentionality to their curriculum. The professors made sure that
everything they taught had its place in the larger narrative, helped me
understand how each lecture fit into the course topic, and how the
course itself fit into the larger field of study. As is pretty apparent
from me deciding to write this article, I like finding and understanding
patterns — it makes sense that the courses I enjoyed the most were able
to weave fabrics out of seemingly disparate threads.&lt;/p&gt;
&lt;p&gt;It’s certainly cliché to say, but I don’t plan on my education
stopping after I get my diploma. Who knows if I’ll ever go back to
school, but I’ll take what I’ve learned about learning itself along
with me as I start my career.&lt;/p&gt;</description><pubDate>Sat, 10 Apr 2021 17:00:00 GMT</pubDate></item><item><title>Hotwiring the Web: What does Basecamp&apos;s New Magic bring to the table?</title><link>https://davi.sh/blog/Hotwiring%20the%20Web/</link><guid isPermaLink="true">https://davi.sh/blog/Hotwiring%20the%20Web/</guid><description>&lt;p&gt;&lt;a href=&quot;https://hotwire.dev&quot;&gt;Hotwire&lt;/a&gt;, which seems to be short for (H)tml
(O)ver (T)he (Wire), is a collection of frameworks just announced by
Basecamp that work together to help build “traditional”
server-rendered web applications that look and feel to users like
modern, Single-Page Applications (SPAs) built in React, Angular, Vue or
other frontend frameworks. &lt;a href=&quot;https://m.signalvnoise.com/html-over-the-wire/&quot;&gt;Basecamp’s CTO put out a blog
post&lt;/a&gt; on why he believes
in Hotwire, but most of the justification seems to be handwavy claims
that JavaScript is inherently “complex,” never mind that Ruby’s
syntax and dynamic type system can be just as head-scratching to a
newcomer. I think that Basecamp’s built a really interesting tool, and
a better argument for Hotwire can be made by fully engaging with the
benefits that SPA “&lt;a href=&quot;https://www.computerhope.com/jargon/t/thickcli.htm&quot;&gt;thick
clients&lt;/a&gt;” bring to
the table, their specific shortcomings, and all the different ways
framework developers are trying to address those shortcomings today.&lt;/p&gt;
&lt;h1 id=&quot;problems-with-web-application-development-today&quot;&gt;Problems with Web Application Development Today&lt;/h1&gt;
&lt;p&gt;I want to separate “web app” development from general “web”
development, simply because most websites aren’t single-page
applications, or web apps in the sense we think of gmail, or
&lt;a href=&quot;https://hey.com&quot;&gt;hey.com&lt;/a&gt;. It’s put most concisely in “&lt;a href=&quot;https://css-tricks.com/how-the-web-is-really-built/&quot;&gt;How the Web
is Really Built&lt;/a&gt;”.
Here, though, I’ll be talking about that subset of the web which &lt;em&gt;can&lt;/em&gt;
be categorized more as an application than simply a web site.&lt;/p&gt;
&lt;p&gt;Building a web app is always going to be a complex endeavour. SPA view
libraries like React have emerged in the past five or so years as an
awesome way to handle that complexity on the client side, allowing
developers to build interactive user-interfaces in a composable way,
re-using components easily in different parts of the site.&lt;/p&gt;
&lt;h2 id=&quot;duplication-of-concerns&quot;&gt;Duplication of Concerns&lt;/h2&gt;
&lt;p&gt;Generally, when building a React app, you get any dynamic data through a
loosely coupled JSON API (in my personal preference, probably written
with &lt;a href=&quot;https://www.django-rest-framework.org&quot;&gt;Django REST Framework&lt;/a&gt;). If
you’re using a web framework like Django or Ruby on Rails, then you’re
going to have a lot of complexity and business logic on both the
frontend and backend. Whenever you add a feature, you need to ask
yourself where it should be added. The loose coupling between parts that
may very well be written in different languages means that it’s really
difficult to share code, and even if you’re doing something as simple
as validating a form, you’ll probably end up with a decent amount of
duplicated business logic, a big no-no if you follow the &lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot;&gt;DRY
principle&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id=&quot;data-fetching-boilerplate&quot;&gt;Data Fetching Boilerplate&lt;/h2&gt;
&lt;p&gt;Loosely-coupled APIs present another challenge when building web apps:
the boilerplate that comes along with getting the data out of your
database and displayed in a browser. The general process for an SPA
follows this outline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;[User] Navigates to a page, submits a form, or clicks on a
resource.&lt;/li&gt;
&lt;li&gt;[Browser] Send a request, along with any route parameters and
arguments, to a backend API endpoint.&lt;/li&gt;
&lt;li&gt;[Server] API endpoint is called, fetches data from the database
using the parameters in the request.&lt;/li&gt;
&lt;li&gt;[Server] After retrieving data from the database, marshal that
data into JSON to be sent back to the client.&lt;/li&gt;
&lt;li&gt;[Browser] Receive JSON from request, un-marshal the JSON into data
structures that the web app can understand. For a React app, this
would include passing the data as props to each component that uses
the data to for render itself.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And that’s only the happy path! In the case of a validation error, for
example, the server would return a 400 response with some JSON details,
and the client again would need to catch that error, and render it
properly. It really is a lot of boilerplate for any given request.&lt;/p&gt;
&lt;p&gt;There are frontend libraries like &lt;a href=&quot;https://swr.vercel.app&quot;&gt;SWR&lt;/a&gt; which
factor out as much boilerplate as possible and encourage declarative
patterns for your own data fetching, but “thick client” web apps will
always need to marshal data to JSON on the server, and un-marshal to
some view-able format on the client.&lt;/p&gt;
&lt;h1 id=&quot;managing-spa-complexity-in-2020&quot;&gt;Managing SPA Complexity in 2020&lt;/h1&gt;
&lt;p&gt;There’s a bunch of different solutions to these problems for those who
still want to provide an SPA experience, and they fall broadly into two
separate categories.&lt;/p&gt;
&lt;h2 id=&quot;frontend-first-the-jamstack&quot;&gt;Frontend-First: The JAMStack&lt;/h2&gt;
&lt;p&gt;In recent years, there’s been a big push towards the
&lt;a href=&quot;https://jamstack.org/what-is-jamstack/&quot;&gt;JAMStack&lt;/a&gt;: pushing all your
complexity and business logic onto your frontend, and relying mostly on
commodified API services for your backend work. JSON is still the lingua
franca here, but reliance on pre-built backends as a service for
different functionalities, along with query languages like GraphQL,
means that there isn’t any backend boilerplate handled by developers.
All the complexity and business logic is pushed to the client-side,
which alleviates worries about duplication of concerns.&lt;/p&gt;
&lt;p&gt;JAMStack and associated “micro-backends” like
&lt;a href=&quot;https://auth0.com&quot;&gt;Auth0&lt;/a&gt;, or “backends-as-a-service” like
&lt;a href=&quot;https://supabase.io&quot;&gt;Supabase&lt;/a&gt; and &lt;a href=&quot;https://firebase.google.com&quot;&gt;Google
Firebase&lt;/a&gt; allow people who haven’t done
too much backend work in the past to build truly full-stack apps on
their own. &lt;a href=&quot;https://m3o.com&quot;&gt;m3o&lt;/a&gt; is even building a constellation of
JAMStack-oriented “micro-services” to provide the batteries to power
most web apps. Hmm… “batteries included”? Where
&lt;a href=&quot;https://www.phoenixframework.org&quot;&gt;have&lt;/a&gt; we
&lt;a href=&quot;https://www.djangoproject.com&quot;&gt;heard&lt;/a&gt; &lt;a href=&quot;https://laravel.com&quot;&gt;that&lt;/a&gt;
&lt;a href=&quot;https://rubyonrails.org&quot;&gt;before&lt;/a&gt;…&lt;/p&gt;
&lt;h2 id=&quot;backend-first-re-discovering-the-batteries&quot;&gt;Backend-First: Re-discovering the batteries&lt;/h2&gt;
&lt;p&gt;MVC Frameworks like Django and Rails came about during the heyday of Web
2.0 to abstract away a lot of the boilerplate associated with building
CRUD web applications, the exact issues that we spoke about above. Back
in 2006, all those interactions were through normal browser HTTP
requests. SPAs, built with anything from
&lt;a href=&quot;https://backbonejs.org&quot;&gt;Backbone&lt;/a&gt; or &lt;a href=&quot;https://emberjs.com&quot;&gt;Ember&lt;/a&gt; to
React and Vue, were more responsive. These web frameworks became
frameworks for JSON API servers, and for many web app developers,
functionality like Django’s
&lt;a href=&quot;https://docs.djangoproject.com/en/3.1/ref/templates/&quot;&gt;templates&lt;/a&gt; and
&lt;a href=&quot;https://docs.djangoproject.com/en/3.1/topics/forms/#more-about-django-form-classes&quot;&gt;forms&lt;/a&gt;
and the battle-tested
&lt;a href=&quot;https://docs.djangoproject.com/en/3.1/topics/class-based-views/generic-display/&quot;&gt;abstractions&lt;/a&gt;
for linking them together became vestiges of an earlier age. Django’s
&lt;code&gt;Form&lt;/code&gt;{.verbatim} classes can render validation errors in templates with
virtually no boilerplate written by developers. As soon as you want to
put that form action over a JSON API, any responses from your server,
which were previously just the HTML that the browser displayed, now have
to be un-marshalled from JSON on the client and handled specifically.
How much was Django really a “batteries included” framework if you
needed to pull in &lt;a href=&quot;https://www.django-rest-framework.org&quot;&gt;REST
Framework&lt;/a&gt; and &lt;a href=&quot;https://github.com/jazzband/django-oauth-toolkit&quot;&gt;OAuth
Toolkit&lt;/a&gt; whenever you
wanted to work with a “modern” frontend?&lt;/p&gt;
&lt;p&gt;Many people, myself included, enjoy modeling business logic in the ways
Django and its ilk allow for. Backend-first fullstack frameworks have
begun to proliferate built on top of these existing frameworks. &lt;a href=&quot;https://www.phoenixframework.org/blog/build-a-real-time-twitter-clone-in-15-minutes-with-live-view-and-phoenix-1-5&quot;&gt;Phoenix
LiveView&lt;/a&gt;
and &lt;a href=&quot;https://laravel-livewire.com&quot;&gt;Laravel Livewire&lt;/a&gt; are two that come
to mind immediately, and have been around for a year or more.&lt;/p&gt;
&lt;p&gt;On Monday, even the React Core team at Facebook threw their hat in the
ring, with their &lt;a href=&quot;https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html&quot;&gt;Server
Components&lt;/a&gt;
that have the opportunity to allow for React components to be rendered
much like PHP templates, interspersing database calls and server-side
JavaScript with the layout description inside a server component’s
&lt;code&gt;render&lt;/code&gt;{.verbatim} function.&lt;/p&gt;
&lt;p&gt;These fullstack frameworks go a long way towards solving both of the
concerns with traditional SPAs listed above. Separation of concerns is
no longer an issue, since there is no separate, loosely coupled frontend
codebase. Data fetching is drastically simpler in this paradigm. No
longer does all your data need to be serialized to JSON before being
converted into HTML; your data-fetching flow looks a lot more like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;[User] Navigates to a page, submits a form, etc.&lt;/li&gt;
&lt;li&gt;[Server] Backend route is called, fetches/stores appropriate data
from the database based on the request.&lt;/li&gt;
&lt;li&gt;[Server] Data is used to populate an HTML template, which is sent
to the client and rendered with the help of the framework.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your backend business logic renders HTML directly, completely replacing
steps 3, 4 and 5 above with a single step: map your data into its visual
representation in HTML. The two server steps remaining &lt;em&gt;are&lt;/em&gt; the actual
business logic in your application: the full-stack framework handles the
smooth transitions, without the developer having to worry about
serializing and de-serializing their own data. The logical flow of your
application becomes a lot simpler for a single developer to follow and
to handle.&lt;/p&gt;
&lt;p&gt;Even React’s Server Components fit this new paradigm: Data fetching no
longer happens in AJAX requests, but by declaring a child server
component which fetches the data and displays it in its own DOM tree
without the developer having to serialize to/from JSON themselves. After
the component renders on the backend, its virtual DOM gets sent to the
frontend &lt;em&gt;by React itself&lt;/em&gt; for display. The developer’s interface into
this whole process remains high-level and declarative.&lt;/p&gt;
&lt;h2 id=&quot;as-the-pendulum-swings&quot;&gt;As the Pendulum Swings&lt;/h2&gt;
&lt;p&gt;We started the decade with frameworks like
&lt;a href=&quot;https://www.meteor.com&quot;&gt;Meteor.js&lt;/a&gt; with extremely tight couplings
between the client and server, and after a long time wandering in the
wilderness of duplicated compelxity across loosely-coupled frontend and
backend, it seems like we’re entering the twenties with a renewed push
towards a &lt;a href=&quot;https://m.signalvnoise.com/the-majestic-monolith/&quot;&gt;more
monolithic&lt;/a&gt; approach
to web development. When even a frontend framework like React is
beginning to bridge the gap with the backend, you know it’s an
interesting idea to explore right now.&lt;/p&gt;
&lt;h1 id=&quot;so-what-is-hotwire&quot;&gt;So what is Hotwire?&lt;/h1&gt;
&lt;p&gt;The folks at Basecamp, the company behind Hotwire, have always been
skeptical of thick clients with loads of JavaScript. Hotwire is
Basecamp’s latest answer to the challenge of building modern,
responsive, “snappy” single-page applications where the domain logic
lives entirely on the server. They used it to build out their new email
service, Hey.com.&lt;/p&gt;
&lt;p&gt;At Hotwire’s core is &lt;a href=&quot;https://turbo.hotwire.dev&quot;&gt;Turbo&lt;/a&gt;, a new library
that takes HTML from AJAX requests and dynamically modifies the currrent
page. It comes out of an existing library called Turbolinks, now called
“&lt;a href=&quot;https://turbo.hotwire.dev/handbook/drive&quot;&gt;Turbo Drive&lt;/a&gt;” as of today,
which is a utility that intercepts all click events on anchor tags,
loads the resources over AJAX, and swaps out the &lt;code&gt;&amp;#x3C;body&gt;&lt;/code&gt;{.verbatim}
tags, all while handling browser history.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://turbo.hotwire.dev/handbook/frames&quot;&gt;Turbo Frames&lt;/a&gt;, one of the
new components, is pretty intriguing. Turbo Drive will still AJAX-ify
form submissions and link clicks behind the scenes, but instead of
swapping out the entire webpage each time, Turbo will look for matching
&lt;code&gt;&amp;#x3C;turbo-frame&gt;&lt;/code&gt;{.verbatim} tags on the current page and in the new
page’s content. If there’s a match, it’ll &lt;em&gt;dynamically replace that
section of the page&lt;/em&gt;. Basically, you can compose webpages together,
using &lt;code&gt;&amp;#x3C;turbo-frame&gt;&lt;/code&gt;{.verbatim} to delineate template partials as
scoped components, similarly to how you’d think of components in a
React app. The benefit here being that all the logic is handled on the
server-side rather than split between two code-bases.&lt;/p&gt;
&lt;h1 id=&quot;trying-out-hotwire-with-django&quot;&gt;Trying out Hotwire with Django&lt;/h1&gt;
&lt;p&gt;What’s special about Turbo when compared to Phoenix LiveView and
Laravel Livewire is that Turbo is completely backend-agnostic: Drop the
JS bundle into a &lt;code&gt;&amp;#x3C;script&gt;&lt;/code&gt;{.verbatim} tag in your page’s
&lt;code&gt;&amp;#x3C;head&gt;&lt;/code&gt;{.verbatim}, and Turbo Drive works its magic without any
co-operation from the server. Turbo Frames can be adapted by wrapping
&lt;code&gt;&amp;#x3C;turbo-frame&gt;&lt;/code&gt;{.verbatim} tags around template partials in any backend
framework. &lt;a href=&quot;https://turbo.hotwire.dev/handbook/streams&quot;&gt;Turbo Streams&lt;/a&gt;,
the solution for incremental data updates, can also be used in the
context of HTTP requests without any co-operation from the server beyond
modifying your template partials. It’s only if you want to use Turbo
Streams over WebSockets where you’ll need some custom code for your
specific backend framework.&lt;/p&gt;
&lt;p&gt;Since the push behind Hotwire came from DHH and Basecamp, it makes sense
that their examples are with Ruby on Rails, and that’s where they’ve
made their supporting libraries. I decided to take a shot at building a
demo app similar to what’s shown in Hotwire’s demo video using Django
rather than Rails. &lt;a href=&quot;https://github.com/davish/hotwire-django-demo-chat&quot;&gt;It really didn’t take
long!&lt;/a&gt; I got my
start in Web Development with a JQuery app with a REST API, and even
after moving on to Django, I always used Django REST Framework. I never
really took advantage of the templating functionality, or the
super-useful built-in CRUD operations with Django forms. It was an
interesting experience working with &lt;code&gt;CreateView&lt;/code&gt;{.verbatim} and
&lt;code&gt;DetailView&lt;/code&gt;{.verbatim} rather than &lt;code&gt;ModelViewSet&lt;/code&gt;{.verbatim}, and I’ll
be excited to keep exploring this going forward.&lt;/p&gt;
&lt;p&gt;After an hour or so more of experimentation and digging into the
turbo-rails codebase, I got a working prototype of a Turbo Streams
&lt;code&gt;Broadcastable&lt;/code&gt;{.verbatim} mixin for Django! I’m working on &lt;a href=&quot;https://github.com/pennlabs/django-rest-live&quot;&gt;something
similar for Django REST
Framework&lt;/a&gt; right now,
which definitely helped in hitting the ground running. I’ll probably
look to clean up the code and make sure it works for the other actions,
and split it out into its own pypi package.&lt;/p&gt;
&lt;p&gt;I’ll have to look at how the Rails integration handles authorization —
right now, anyone would be able to subscribe to any stream for a given
model, which is obviously not ideal for actual production applications.&lt;/p&gt;
&lt;p&gt;I was really surprised at how easy it was to set up Turbo to work
effectively with a Django backend. The chat app that I built was really
simple, but it also was just not many lines of code: not having to worry
about moving data around from the frontend to the backend really
decreased the amount of time spent on the implementation. Turbo though
is in a pretty early beta, and the one main thing I’d like to see be
addressed would be a good fallback mechanism for Turbo Streams over
websockets. Right now, if you want to broadcast updates over websockets,
then you can’t also send Turbo Streams in HTTP responses to form
actions without getting duplicate data appended. The solution in the
Hotwire demo video is simply to not send updates over HTTP, and only
stream over websockets. This doesn’t seem particularly robust, however,
in the case that a websocket connection fails or a client simply
doesn’t support it. In addition to the five actions, there should
probably be an &lt;code&gt;append-or-replace&lt;/code&gt;{.verbatim} action that looks for an
element with a matching &lt;code&gt;id&lt;/code&gt;{.verbatim}, performs a &lt;code&gt;replace&lt;/code&gt;{.verbatim}
action if one is found, and otherwise performs an &lt;code&gt;append&lt;/code&gt;{.verbatim}
action. The duplicate updates from the HTTP response and the websocket
stream wouldn’t conflict in that case, since one will append, and the
other will replace with identical data.&lt;/p&gt;
&lt;h2 id=&quot;closing-thoughts&quot;&gt;Closing Thoughts&lt;/h2&gt;
&lt;p&gt;This is definitely an exciting time for frontend development! I’m
hoping to do some more experimentation in the coming weeks, and I’m
glad that framework authors accross the board are putting effort into
thinking about how to move the web app developer experience to the next
level.&lt;/p&gt;</description><pubDate>Tue, 22 Dec 2020 23:38:55 GMT</pubDate></item><item><title>Lessons Learned from Leading Penn Labs</title><link>https://davi.sh/blog/Labs%20Looking%20Back/</link><guid isPermaLink="true">https://davi.sh/blog/Labs%20Looking%20Back/</guid><description>&lt;p&gt;I just finished writing &lt;a href=&quot;https://pennlabs.org/blog/year-in-review-19-20/&quot;&gt;a year-in-review blog
post&lt;/a&gt; for the Penn Labs
blog. For some context, &lt;a href=&quot;https://pennlabs.org&quot;&gt;Penn Labs&lt;/a&gt; is a
student-run software development group that builds apps and websites to
help students navigate life on campus. For the past year, I’ve been one
of the two co-directors of Labs, managing the operations and direction
of the organization.&lt;/p&gt;
&lt;p&gt;I wrote the year-in-review post as a way to highlight the awesome work
the whole team had done this year, and hopefully start a tradition of
outgoing directors doing the same in the future. While I was writing it,
though, I started thinking about what my personal year-in-review would
look like. I’ve written down my thoughts here, in the form of different
lessons I hope to take with me going forward.&lt;/p&gt;
&lt;h1 id=&quot;delegate&quot;&gt;Delegate&lt;/h1&gt;
&lt;p&gt;Many clubs at Penn operate with a governing board structure. Labs
currently doesn’t have any sort of student board outside of the two
Co-Directors. Because of this, the Co-Directors are directly responsible
for everything from managing finances to grabbing supplies for group
work sessions to heading up recruiting efforts. All of these are
critical to the club functioning properly, but it’s also a ton of
extremely different things for only two people to juggle in their heads
at once.&lt;/p&gt;
&lt;p&gt;This year as director really made me realize the power of delegating
responsibilities. We’re lucky enough in Labs to have a ton of team
leads and members who are just as committed to the organization as the
directors are. Taking advantage of members’ enthusiasm to spread the
work around is a really good way to alleviate stress and take some work
off my plate.&lt;/p&gt;
&lt;p&gt;An example that I’ve caught myself with a lot is email. We get tons of
messages regarding specific products that we run. The directors could
worry about all of these, but it’s much more time efficient for us to
delegate to team leads regarding their product. It may seem trivial and
obvious to do, but I find if kind of difficult to stop myself from
digging into research required to answer each email we get.&lt;/p&gt;
&lt;h1 id=&quot;communicate&quot;&gt;Communicate&lt;/h1&gt;
&lt;p&gt;One of the ways to view the director role is as a manager-of-managers.
Directors are in charge of keeping up with team leads and making sure
they have the resources they need to lead their teams. Something my
co-director and I made standard this year was meeting with every team
lead individually to get an update on how their team was working
throughout the semester.&lt;/p&gt;
&lt;p&gt;While these were certainly intended as avenues for us to get feedback,
we were sometimes surprised by points that leads brought up in these
on-on-ones which seemed relatively urgent, yet were not raised
beforehand. It’s easy to wonder exactly what we were doing wrong to not
appear as open to talk to. However, I think the answer lies more in
human nature. When people have issues, especially those with leadership
responsibilities, it’s natural to try and resolve it on your own. It’s
up to us as managers to keep in sync with our direct reports and expect
that we’ll sometimes need to “pull” information rather than get it
“pushed” to us. Sometimes, the birds-eye view that Directors have in
the organization can be really useful for working through an issue that
a specific team is facing.&lt;/p&gt;
&lt;p&gt;The need for communication also manifests itself during the recruitment
cycle. Directors manage the recruitment process. But it’s important
before the process starts for us to have a good idea of what individual
team needs actually are. If it turns out no team really needs backend
web developers, we should think about how and if we should recruit for
that role.&lt;/p&gt;
&lt;p&gt;Final deliberations and team assignments can also get contentious if
talent for a particular role is scarce. It’s up to directors to mediate
between leads and find a fair solution that everyone can live with.&lt;/p&gt;
&lt;h1 id=&quot;think-big-picture&quot;&gt;Think Big Picture&lt;/h1&gt;
&lt;p&gt;At this point, Penn Labs runs about 10 services across six product
teams. Before I was director, I was a team lead for Penn Courses, which
solely manages products surrounding course registration. My largest
change in thinking going from team lead to director was having to keep
the priorities of all the different teams within the organization in
mind whenever making a decision. This ties in well with both themes
mentioned above. The only way to get the whole picture is to
continuously communicate with team leads and general members about where
they see their projects going. And the only way to lead a whole
organization effectively is to advise leads while trusting them to make
the right decisions.&lt;/p&gt;
&lt;p&gt;It can be tempting to try and force projects in one direction or
another. But I found it important to keep my eyes on the big picture and
leave it up to leads to sweat the smaller things. The more minutiae
directors manage and worry about, the less context individual leads have
to deal with problems that inevitably come up when the directors don’t
have the bandwidth to handle it.&lt;/p&gt;
&lt;h1 id=&quot;think-long&quot;&gt;Think Long&lt;/h1&gt;
&lt;p&gt;Maybe not as long as the &lt;a href=&quot;https://longnow.org/&quot;&gt;Long Now Foundation&lt;/a&gt;,
but thinking on a year-plus time horizon is an important responsibility
for directors specifically. Not many software organizations have 100%
turnover every three or four years. In industry, it’s common to be able
to send a Slack message or walk over to the desk of the person who wrote
the code that you’re now tasked with changing. As a student
organization, all our members graduate and move on a very regular basis.
It’s extremely important that we’re able to transfer knowledge
effectively from older members down to younger members.&lt;/p&gt;
&lt;p&gt;It’s up to directors to remind developers and leads of this reality.
Documentation is always in the back of my mind. Written docs aren’t a
panacea, but in my opinion, they can’t hurt when the alternative is
trying to decipher code written years before. We’ve also started asking
leads in their one-on-ones to make sure they have at least one younger
member in mind to mentor and ultimately to replace them.&lt;/p&gt;
&lt;h1 id=&quot;recruit-with-intention&quot;&gt;Recruit with Intention&lt;/h1&gt;
&lt;p&gt;In my opinion, recruitment is the most important job that directors
have. Considering that the 25%/year turn-over rate is a reality that we
have to deal with as a student organization, it’s important that we
take recruitment seriously and look for prospective members who can
contribute a ton to the club, both from a technical and community
perspective.&lt;/p&gt;
&lt;h2 id=&quot;mentorship-and-community&quot;&gt;Mentorship and Community&lt;/h2&gt;
&lt;p&gt;This year, we brought in somewhere around 25 new members in the Fall,
doubling the size of the club to 50 members total. All these roles were
filling needs that teams saw themselves having, especially with a
sizeable senior class graduating in the Spring. While team leads
definitely had work for all our new members to do, we didn’t
necessarily foresee the reality that if there was a one-to-one ratio
between newbies and oldies, then &lt;em&gt;every&lt;/em&gt; oldie would need to be a mentor
to a newbie.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hbr.org/2015/03/6-rules-for-building-and-scaling-company-culture&quot;&gt;There&lt;/a&gt;
&lt;a href=&quot;https://about.crunchbase.com/blog/scaling-culture/&quot;&gt;are&lt;/a&gt;
&lt;a href=&quot;https://www.culturesummit.co/articles/scaling-culture-to-1000-employees/&quot;&gt;tons&lt;/a&gt;
of articles online about the difficulty of scaling company culture. Labs
is no different here. We pride ourselves on our sense of community, and
our rapid growth made it easier for new members to get lost in the fray.&lt;/p&gt;
&lt;p&gt;When thinking big, it’s also important to think holistically. There’s
obviously the need for technical bandwidth, but it was important for us
to balance that with our goal to be an amazing and welcoming community
on campus.&lt;/p&gt;
&lt;h1 id=&quot;prove-your-concept&quot;&gt;Prove your Concept&lt;/h1&gt;
&lt;p&gt;By the nature of the projects we’re working on, we meet pretty
frequently with school administrators. To me, one of the most
interesting parts of the job has been getting to see the inner workings
of how the University makes decisions. It’s not always the most
exciting process, but I’ve learned a lot through being director about
how to get your point across to key stakeholders who don’t necessarily
need your help as much as you need theirs.&lt;/p&gt;
&lt;p&gt;One thing that I’ve found is that an MVP is a powerful tool. One of the
team leads on the Penn Mobile project was able to give students the
ability to book Wharton study rooms through our app by
reverse-engineering the web-based API. This was super brittle: and any
change to the first-party website’s flow would break our integration as
well. We got in contact with the IT division in charge of developing the
room booking system. After we showed them the demand that students had
for using our mobile app, they agreed to work with us to build an
official API integration. It’s not for certain, but I really believe
that showing that our reverse-engineered solution was popular was a big
part of us getting our foot in the door in those discussions. A cold
email can certainly go a long way with the right recipient, but showing
that you’ve already put in sweat to start solving a problem can really
get your foot in the door.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Obviously, a lot of what I wrote about above is particularly relevant to
leading Penn Labs, and maybe slightly more broadly, to leading a club at
a university. But I think I’ve learned some more generally applicable
things about leadership along the way.&lt;/p&gt;
&lt;p&gt;If I could sum it up in one sentence, I’ve learned that a big part of
being a leader is being proactive: reach out to reports to check in;
take the initiative to prove out ideas before pitching them to decision
makers; think ahead and anticipate challenges to confront them head-on.&lt;/p&gt;
&lt;p&gt;I’ve loved being director. It’s helped me give back to an organization
that has been a huge part of my college experience. I’ve still got a
year left in the club, and I’m excited to see where it all goes from
here.&lt;/p&gt;</description><pubDate>Mon, 25 May 2020 16:15:00 GMT</pubDate></item><item><title>Switching to Emacs</title><link>https://davi.sh/blog/2020/03/switching-to-emacs/</link><guid isPermaLink="true">https://davi.sh/blog/2020/03/switching-to-emacs/</guid><description>&lt;p&gt;My personal editor journey has been kind of strange. I started off using Sublime
Text for most things, and then switched over to JetBrains IDEs on a student
license. JetBrains IDEs are pretty amazing — especially when it came to PyCharm’s
Django support. One thing I started to worry about recently has been my reliance
on a closed-source, and, once I’m out of college, pretty expensive editor. Sure,
many employers pay for licenses, but it would be awesome if I could configure
an editor that worked well for me and was also completely open-source.&lt;/p&gt;
&lt;p&gt;A few months ago, I switched my Python development to VSCode, where I had
already been doing frontend JS/TypeScript dev for a few years. It started up
faster than PyCharm and had extensions for most things, but I never really felt comfortable.
After learning vim keybindings this summer, I would’ve definitely liked to also
be using the keyboard more often. Primarily, though, I’d heard really cool
things about &lt;a href=&quot;https://orgmode.org/&quot;&gt;org-mode&lt;/a&gt;, and wanted to try it out.
Full support of org-mode is only present in emacs, which in the midst
of quarantine would be a bit of excitement to check out.&lt;/p&gt;
&lt;p&gt;At the recommendation of a friend, I decided to use
&lt;a href=&quot;https://github.com/hlissner/doom-emacs&quot;&gt;doom-emacs&lt;/a&gt; instead of starting a
config from scratch. I knew nothing about emacs, and after my experience trying
to start a vim setup from scratch over this summer, I knew I wouldn’t have the
patience to learn &lt;em&gt;everything&lt;/em&gt; I needed to about emacs lisp and package
management before jumping in. Doom gives opinionated defaults for those
comfortable with vim and its keybindings and lots of curated “modules” which
wrap around existing emacs plugins. While the existing getting started docs are
&lt;em&gt;comprehensive&lt;/em&gt; for sure, &lt;a href=&quot;https://medium.com/urbint-engineering/emacs-doom-for-newbies-1f8038604e3b&quot;&gt;this
article&lt;/a&gt;
was really a great introduction to get the ball rolling for me.&lt;/p&gt;
&lt;p&gt;And honestly, it didn’t take long at all to get a setup where I feel like I can
be productive. I make heavy use of goto definition when I’m coding, so I knew
I’d need that functionality. Luckily, doom has easy support for the Language
Server Protocol, same as VSCode does. All that’s required to enable it in Doom
is to add the line &lt;code&gt;(python +lsp)&lt;/code&gt; to the &lt;code&gt;doom!&lt;/code&gt; block in your
&lt;code&gt;.doom.d/init.el&lt;/code&gt; file. The Python language server was a bit finnicky, but after
setting it up, along with JavaScript, I’m already at a point where I can be
productive. I enabled a few more Doom modules, like a terminal emulator
(&lt;code&gt;term&lt;/code&gt;) and a file explorer (&lt;code&gt;treemacs&lt;/code&gt;). Here’s where I’m at, about a day and
a half in:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/blog/images/emacs-screenshot.png&quot; alt=&quot;Configured Emacs Screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can see the VSCode inspiration pretty clearly here! Another thing that’s
cool about Doom is that in a lot of ways, it’s an entire configuration platform.
While it comes with a lot of modules curated by its author, you can easily add
modules yourself with your own configuration. It’s something expanded on in
&lt;a href=&quot;https://yiming.dev/blog/2018/01/22/compare-doom-emacs-spacemacs-vanilla-emacs/&quot;&gt;this article comparing Doom and
Spacemacs&lt;/a&gt;,
another emacs “distro”. I’m not sure if I’ll continue using Doom long-term as I
learn more about emacs and how to configure it. There’s a big possibility I jump
to using a more “vanilla” experience, with only the packages I know I use. Only
time will tell here!&lt;/p&gt;
&lt;p&gt;But one thing I’m really happy about is how much less I’m relying on the mouse
than I was in either VSCode or any JetBrains IDE. I’m able to navigate around
the entire editor and go through file trees from the keyboard, thanks to great
VIM keybindings provided by &lt;code&gt;evil-mode&lt;/code&gt;. I haven’t even touched org-mode yet,
and I’m already glad I made this transition. I’m looking forward to learning
more in the coming weeks!&lt;/p&gt;</description><pubDate>Tue, 31 Mar 2020 21:58:32 GMT</pubDate></item><item><title>At Home</title><link>https://davi.sh/blog/2020/03/at-home/</link><guid isPermaLink="true">https://davi.sh/blog/2020/03/at-home/</guid><description>&lt;p&gt;Last Wednesday, Penn &lt;a href=&quot;https://www.thedp.com/article/2020/03/penn-coronavirus-online-classes-spring-semester&quot;&gt;extended Spring Break by a week and moved classes
online for a semester&lt;/a&gt; in response to the threat of COVID-19.
The same day, the WHO declared the virus’s global spread officially a
pandemic. In the days since, I’ve packed up my dorm room and moved home.
I’ve tried
to see how I could use this time effectively — tackling projects I wouldn’t
necessarily have time for in a busy college schedule, and learning new
skills. Here’s a good a place as any to jot down some of my ideas, and
hopefully it’ll keep me somewhat accountable.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Re-make &lt;a href=&quot;https://when2meet.com&quot;&gt;When2Meet&lt;/a&gt;. It’s a super simple and well-liked
tool on campus, and I think it would be a cool experience to see how I’d
make it, and if I could find any ways to improve on it while keeping its
simplicity. I’ve been interested in functional programming lately, so I
think I’ll try to tackle this using &lt;a href=&quot;https://reasonml.github.io/&quot;&gt;ReasonML&lt;/a&gt; and &lt;a href=&quot;https://elixir-lang.org/&quot;&gt;Elixir&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Learn iOS development. Something I’ve been thinking about for a little
bit. I’ve been enjoying frontend development more lately, and it might
be cool to add another skill to my toolbox on this front, especially
with how many product ideas are mobile-first.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Keep working on &lt;a href=&quot;https://pennlabs.org&quot;&gt;Penn Labs&lt;/a&gt; products. With students
scattered around the globe, tools like &lt;a href=&quot;https://penncoursealert.com&quot;&gt;Penn Course
Alert&lt;/a&gt; and &lt;a href=&quot;https://penncourseplan.com&quot;&gt;Penn Course Plan&lt;/a&gt; are
going to be even more critical to students. We’re hoping to release a new
version of Penn Course Alert with some new and helpful features before
Penn’s course registration starts, and that deadline should help keep me
motivated and moving.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Improving this site. This only just went up a week ago or so, and right
now is still using a pretty vanilla theme. I’m planning on adding some
features like a project showcase and spicing up the theme a bit, with some
graphics and color.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description><pubDate>Wed, 18 Mar 2020 02:11:32 GMT</pubDate></item><item><title>My First Post</title><link>https://davi.sh/blog/2020/02/my-first-post/</link><guid isPermaLink="true">https://davi.sh/blog/2020/02/my-first-post/</guid><description>&lt;p&gt;Hello, world! I just bought a domain name today (&lt;a href=&quot;https://davi.sh&quot;&gt;davi.sh&lt;/a&gt;) and am setting up the site.
We’ll see how this goes! Hopefully it can get me to start blogging about cool stuff :)&lt;/p&gt;</description><pubDate>Fri, 28 Feb 2020 03:23:37 GMT</pubDate></item></channel></rss>