
War Isn’t Murder by Jesse Wells showed up on my feed earlier today it has me excited for some proper anti-establishment music again. Lindsay Ellis’s perhaps singular good video, Protest Music of the Bush Era explains how the punk-rock genre evolved from the invasion of Iraq, and leaves us wondering why we haven’t gotten such music in the in response to this newly-defined-but-not-new era of Trump.
In the past decade, we’ve only heard the opinions of artists from their socials or interviews. They’ve neglected using their art as a means to express these opinions. Those more doomer pilled than I will say that’s that because they don’t actual hold any beliefs, it’s all just PR, liberal guilt, or the fear of being cancelled if they “stay silent”. I personally don’t think it’s any of these things, I think that the necessity of a response came much quicker than anyone was prepared for one. They’re thinking, between 2016 and 2024, that it’s all a blip. All of our problems are temporary, this isn’t some massive cultural shift towards evil, it’s just something we have to get through and once we do it’ll be nice and convenient to talk about how you were morally correct during that one interview with Kimmel. I’m not saying “that’s not enough” in the sense that these people have power and they should be doing more to advocate for [my preferred policy positions here], I’m saying that it’s not enough because you cannot transpose your celebrity for your art.
That’s a lie that social media has told the artists: you got your voice by being a talented artist and somehow you can leverage that audience in something… that isn’t art. A blog post isn’t art, a twitter thread doesn’t count, and no number of black-background-with-text instragram posts will ever reach the power that a nicely written and produced song or short film does. That’s why you’re here, if you have something to say, you have no venue to make your thoughts known other than where you came from, hopefully what you’re good at.
Everyone was caught off guard with Trumpism and the right-wing new media. Thankfully for them, they can’t actually create art because their entire philosophy revolves around destruction and restriction. What film has been released with a right-wing narrative that was deep art in the same way Eyes Wide Shut or The Menu was? If you don’t agree that those are political movies, that’s my point. They aren’t subtle, they’re not trying to hide their ideology, they’re just good stories written from a certain reference point. And maybe your analysis says something different than what I thought, or that the writers intended, and that’s also beautiful.
There’s something I found in writing software really surprising. If you spend more than fifteen seconds in software, you have necessarily written something that likely no one else has written or has thought to write before. That’s why it’s a creative endeavor and if you want a computer job without needing a creative drive, do IT or cybersecurity or something. The second you make something interesting, it’s because it’s been made for the first time. I guess that’s why I find artists turning to out-of-art statements to express opinions so troubling. Millions of people at any given time have the exact same opinion as any given singer-songwriter, but no one else can sing exactly like they do. They’ve created this arena for themselves where people pay them to sing, but all of a sudden there’s nothing of substance? But you just said on Kimmel how much you care!
Footnote:
The Me Too and Time’s Up saga existed entirely off screen. I take that back, actually. Bojack Horseman’s fifth season was basically dedicated to Harvey Weinstein, and though Bojack we learned that apology and changing your behavior sometimes isn’t enough. I personally feel that season wasn’t its best because it was less-than-subtle even for Bojack standards, but it was exactly the thing I’m describing so I’d like to acknowledge that it can be done and done well.

It wasn’t until Summer of 2022 that I found my first passion in programming (or, Software Development if you’re a corporate shill like myself). It’s the basis of not just most of my posts but most of my desire to post. It’s the secondmost reason I get out of bed on the weekends. I think about it constantly, partly because I’m constantly looking at screens, but more optimistically because in my bones I find so much joy and purpose out of creating software. I take this stuff seriously, because what other mode of operation is there?
In 2022, I was in the Army and my proper job was working and maintaining satellite dishes and the equpiment that surrounded them, but I spent most of my time on networking. Routing, switching, firewalls, generic IT stuff like that. We did helpdesk for our users, and it wasn’t “turn it off and back on again” kind of troubleshooting, this is one of the most computer-skilled positions that a junior enlisted person is eligible to have. Like every unit in the Army, we got rounds of new people and lulls of time where we didn’t gain or lose any teammates for months at a time. Every so often, there would be a round of folks straight out of AIT and there would be one or two of them that had this mystical ability to pay attention and care about the technical details, even if they did not yet fully understand them. Our job was simple enough that someone who cared would be able to learn and be a 10x helpdesk technician quite easily.
I started the helpdesk position in 2019 with gamer-level computer knowledge. But, I found the problems interesting and I had the best mentour I could ask for as a team lead. The diagnostics, the ability to flex technical prowess, the immediate feedback loops that what I did helped someone complete their mission. It was incredible. Probably my favorite part was talking to the people out on the field. Our shop was filled with people of varying degrees of ability and dedication, but at least we had the tools and the time to do some amount of training to raise the floor for the squad. On the other end of the phone, those guys were even more unequipped, untrained, and had on average less technical people. When I needed them to run a command on their switch using PuTTY, I paired that with a cursory training on what that command would tell us and why it’s important. Users would often fake their terminal outputs to us because they thought it would get them to some more advanced stage of troubleshooting, even though this IS the advanced troubleshooting! You don’t lie to your doctor for a reason. I don’t care if it’s your fault or my fault, and you as a user shouldn’t care either. So, by taking an extra step, dipping into some fundamental understanding of networking and how their network relates to ours, I can convince them to trust me.
I felt eternally locked in while I was doing 12 hour shifts of helpdesk. I became the most advanced technician on my shift, and it remained that way until I left for my next duty station. This was the first time I was able to roll with the other people in the room who “got it”. When I was in basic training, I just didn’t get it. I was clumsy, constantly making small mistakes, I took too long to shave when we were out in the field, I felt enternally lost. I have an older brother and half of the time I would look up to him on what to do and the other half I would look up to him for what not to do. I was able to see the other people in my cohort that didn’t “get it”, and the ones that did and I tried to emulate those that did with little avail. I could definitely tell the difference when I saw it, but I couldn’t understand what it is. For me, if there were clear directions and I knew how I could help, you bet your ass I would push myself to do my part. The other guys that didn’t get it, well they were just lazy. Even if they were told to do something directly, like filling up an ice box, they would begrugingly do it in the most lackidasical fashion. You know you signed up for this right? I wasn’t lazy, it’s just that if either of those things weren’t true: if there weren’t clear directions, or I didn’t immediately know how exactly where to insert my efforts, I would stand around waiting on someone who knew to instruct me. This has the same appearance of laziness, but for me it was more like I felt that doing something incorrectly had a greater amount of risk than doing nothing. Often times the person that “knew” and could get me out of this rut had exactly the same amount of information that I did.
In Season 1 of Suits, the young imposter lawyer Mike Ross doesn’t get it yet. He knows he’s smart, his whole backstory is about how he wins just by showing up because his smarts have been able to carry him. He is, in the story, always the smartest person in the room and this led him into mediocrity. As Harvey hires him, he hasn’t ever been challenged before, and that leads to so many interesting moments where he’s smarter than Harvey, but he’s not capable of rising up to Harvey’s level. In one episode, has an argument with Harvey about having to wear a suit to work, even when no clients are around. How rediculous! This has nothing to do with research and strategizing, and isn’t that what lawyers do? The chicken and egg for him is that caring about your appearance is a logical extension of being a good lawyer. But he doesn’t know that because he is, in fact, not a lawyer. He never had any previous intersted in being a lawyer, because he never developed a passion for it (or anything for that matter). But what Harvey understands is that it was never about the suit, it’s the motivation behind it. And most of the time, acting like the best in the business comes before being the best in the business. They go back and forth on things just like this, and eventually Mike gets it. Then, he’s off to the races. He handles his first solo case representing someone who is being evicted, and he develops a dedication to the craft. Harvey obviously has the skills to mentour him, but it’s not until Mike is “in it for real” that he is capable and worthy of this level of attention.
For Army folks, the litmus test is whether or not you ever put your hands in your pockets while in uniform. It’s a rule, has been a rule for decades, and no one follows it. It’s the thing you begrugingly acquiese to when a senior NCO knife-hands you so you don’t have to do push-ups. The senior NCO, by definition, is bought into the system. They have agreed to the lifestyle of the Army at least twice in their career, and you only do that if you have internalized customs like these. You not just agree with the rules, you accept them personally. Most of these people will end up parenting with the same leadership style that they utilize in the military. It’s who they are. They care about hands in pockets not because they are told to do so, but because to violate such a basic rule in front of them is an attack on them personally. You are telling them “I bet you’re not in this for real,” with such confidence that you are risking getting in trouble! How offensive! The rule is arbitrary by any standard, but a violation of it isn’t.
When you ask someone why they joined the Army, a lot of them will say something to the effect of “I needed discipline in my life”, and I think this is what they are referring to. The unfortunate part is that you can’t have this personality trait beaten into you. For me, I was put in positions where I rose up to the challenge and that’s how I developed myself personally. It’s not automatic!
My favorite Dave Ramsay expression is explained in this talk: “with gazelle-like intensity”. He’s a personal finance guy, and he’s telling you that you need to avoid debt like the banks are lions and you are a lowly gazelle. If that gazelle doesn’t RUN, doesn’t register in their brain that this is a life or death situation, the gazelle WILL get eaten. There isn’t any amount of accounting, diagnostics, or studying that will motivate you like a gazelle running from a lion. He’s trying to give other people “the stuff”, not just the fundamental knowledge but the personal dedication that’s required to integrate these principles into one’s life. The phrase is a testament to what’s required to follow these rules when no one is looking. You have to see the banks and the credit card industry as the predators that they are, and you have to flee with gazelle-like intensity. The talk awoken something in me. First, it got me to pay off my car that had a 7% interest rate with four years left, but also I saw that having “the stuff” is not restricted to being in the Army or programming, it can be something felt by anyone about anything. His show is mostly consists of call-ins where regular people ask him for advice regarding their specific financial situation. Sometimes, the caller knows that they have a spending problem, and when Dave says “I think you have to sell you half-million dollar house and cut up your credit cards”, the caller recoils that that is not an acceptable solution. What else is there to do? Did they think there’s a solution to a spending problem without making sacrifices? To spend more than you make is by itself not logical, and therefore you cannot solve it with logic alone. You have to get ANGRY!
That feeling, the passion, the aspiration, and the contrary thereof, is what I’m trying to communicate in this post. Without these anecdotes there aren’t any combination of words that I can use that will abstractly define the concept. It’s not plainly intuition, it’s a recognizition that you don’t have it and you have to develop yourself as a person to acquire it. It’s what my team lead saw in me when I arrived in the unit in 2019, that led him to spend hours of one-on-one troubleshooting sessions with me over his shoulder. This personality trait is what led me to listen and take seriously the material I was being taught, and then later made me feel responsible for teaching the next generation of techs. It’s what brought Mike Ross from being a stoner loser who happened to be smart to a locked in lawyer who is able to hang with the big boys. It’s what Dave Ramsay is trying to get people to, not understand, but feel towards their own money. It’s what I look for whenever I have the priviledge of mentouring someone.
Because I feel this way towards software, I am constantly annoyed whenever I see a desktop app that is actually webshit. Performance matters. User experience matters. In the current generation of software, these two concerns are becoming one. At some point, there was an argument to sacrifice perf for user experience. In order to create new features, you computer does in fact need to do some amount of work. But, now the scale has gone so far that performance is UX. The thumbnail of this post is what happened to me yesterday where I went to change the volume on my computer, and it wouldn’t load. Natalie and I would argue about whether or not React Native counted as webshit. I still think that RN has promise and is full of a team that cares extremely about performance, but “write once run everywhere” and a Javascript runtime of any sort has an inherent cost that cannot be fixed. Maybe that cost is minimal, and the team over there can finally ship their 1.0 and it has acceptable perf characteristics. This experience with Windows tells me, not just that the operating system has incredible tech debt that they have no interest in correcting, but that the Microsoft as a company doesn’t have “the stuff” necessary to fix it with gazelle-like intensity. Whether that’s because their engineers are incapable because of years of bad hiring/promotion/culture, or because the ones promoting performance over developer experience are thwarted by project managers that care more about adding features at whatever cost, it doesn’t matter. It’s either an organizational embarrassment or a testament to how it’s an impossible problem to solve at the scale of a project like Windows.
This is why startups are able to eat the lunch of larger corporations even though they have 10% of the employees and 1% of their budget. Startups are able to hire based on personality and only among some small group of people, and the ones hiring know the candidates well enough to determine whether they take their work seriously. Maybe this can’t scale because there isn’t an interview question that will differentiate someone who has it versus someone who doesn’t. And for a larger organization, how do you test that the hiring managers are capable of sniffing out those who do and those who don’t? How do you tell if someone is incapable versus someone who is capable but hasn’t been given a favorable environment? I have theories, ideas, on what I would ask to a potential candidate, but as I have literally zero hiring experience, I can’t speak to this.
One of the most hirable qualities in someone, that I can define, is someone who takes ownership of their organization even if they are at a junior level. When I took a helpdesk call and someone said to me “You guys messed up my network, how could you!”, I didn’t say “Well, Sergeant, I didn’t do that, it was someone else, actually. You should talk to them.” No! I apologized, I represented the unit because that’s what my role was. Just as when I talk to users of our AV systems, I don’t say that there’s a bug in Crestron’s shitware and what I have provided you is the best pile of shit I can deliver given the system, I take their complaints seriously and try to ease the tension by providing some kind of solution. Passing blame among the most dishonorable qualities I find in someone. Own your stuff, unless you’re talking to someone in the chain that can take responsibility.
It helps if you personally agree with the mission that you are working on, but it’s not necessary. In the Army, I agreed with what our mission was, and I understood it as extremely important. Now, as an AV guy, I don’t find personal satisfaction in making someone’s conference room extravagant. In fact, most of the customers I work with are government, so I don’t even know what it is that they do. I suspect that I would actually disagree with what they are doing with the enhanced collaboration capabilities. It could go either way, and there’s no way I’ll ever truly know what happens in those rooms after I leave. In any case, what motivates me here is… I don’t know. I can’t not. I don’t have any other mode of operation but to take my work seriously. I’m not burnt out, I’m surrounded by a fantastic crew, all of the project managers I work with don’t understand what I do so I get to do whatever I want, and on some level I’m happy that I get paid to do software development at all. My boss took a big risk signing me on, betting that I could learn the job in a record-pace time, and I’m extremely grateful for the opportunity to start a career in software. By all standard metrics, I should hate it. I work long hours, during holidays, I travel a lot, sometimes without any reason other than “someone has to do it”. The worst part about the job is that there isn’t any other programmer that I can talk with. Anyone, more senior or more junior, would be great to have. I just want someone to collaborate with this stuff on. I can ask my coworkers on if a UI design makes sense, but they a) are too polite to tell me it’s shit, and b) don’t have the design skills to assess why it’s shit, so I don’t ever get good feedback. It’s not that much pressure, really, because the project scope is typically capable of being made by a solo person, but I am spiritually starving for a coworker that has “the stuff”.
On the other hand, I get to solve unique challenges on varying projects of varying sizes and complexities, I get the satisfaction of interacting directly with users face-to-face, and lately I’ve been able to hone in my web/react skills. Don’t let my earlier comment misrepresent me, I love the web, warts and all, and I really enjoy React as a programming paradigm. Right now I’m working on an electron+react application, but the difference is that its responsibility is to literally be a GUI that takes in user input and spits out JSON file, used by one person, maybe once a month, for 20 minutes. Not exactly the same as “This is where 100% of your music listening lives now”. This post isn’t an argument about perf because it’s not always about perf, sometimes shipping something cheaper (i.e. less hours worked on it) is better than perf or even UX. But, inconveniently, it’s also the mark of a bad programmer to choose something that is easy when it should be performant or native to the platform. I don’t trust companies to be able to tell the difference anymore, and it’s possible my judgement is flawed.
Thanks for reading, I hope I articulated this feeling well. My preferred verbage for this concept is “cracked”, and I’ll probably use that word in the future. But I wanted to try to give some kind of legitimacy and context to the word beforehand, because as a word gets more popular, especially among software devs, the more watered down it becomes.

A few months ago I posed the idea to Natalie on getting an FM radio license. We didn’t talk much about it, as if we were in such agreement that neither bothered to ask any details - and given the number of excuses I’ve been handing out to friends and family over the last few months, it remains an idle curiousity.
We play public domain/open license music. Podcasts and talks from yesteryear about our favorite topics. As hosts, we could have recurring bits of proper old school radio delight, minus the cheesy sound effects. We would have control on sponsorship, if any at all. I assume that an FM license is cheap to obtain and cheaper to maintain, but maybe this is a bad assumption (bands are scarce!). Anyways, it’s been a little less than idle lately. I’ll find myself dreaming of fostering a community of like-minded technologists, talking with Natalie the way we normally already would, but broadcasted. A recording? NO! You either catch it live or you miss it. And as for who is actually listening at any given moment: that’s the beautiful thing about radio! You have absolutely no idea. You can have signals, like surveys and other call to actions, but you will never know a minute to minute accounting of how much folks are listening to you right now. It’s freeing because there isn’t a chat, no count, no feedback at all that what you’re saying is worthwhile. I imagine it like a silent auction: either put in your best offer or bust.
Why on earth would I step into a medium of which I am not just unfamiliar, but totally uninterested? I couldn’t tell you the last time I listened to the radio, on purpose, for more than 10 minutes. Every time I tune in, I get hopeful because I rediscover the fact that radio actually does have nice variety. But, no matter how great it is I don’t come back because my phone is just technically better. I want to like radio, because they have DJs! Real humans that talk throughout their time on set. But, I think radio has lost this edge, even to the soulless algorithm. I’m a pop girly, and of course those are the worst offenders with the sound effects and staged call-ins, top 10 songs that get replayed to accomodate for the average American’s ~30 minute commute, and weaved between ads and segments, weather and traffic reports! But even of the stations that aren’t as cringeworthy, no matter what they will still have advertisements and that’s an absolute no-go for me. I pay for my music so I don’t hear ads. I want to like radio, but the environment makes it impossible. And, if I’m not a radio listener, why should I be a radio broadcaster?
We have this blog, and honestly I don’t keep track of anyone else’s blogs. I never really understood RSS until Natalie showed me her preffered reader, and even now I struggle to muster the attention span to browse through blogs whenever I come across them. I excuse my lack of interest in the blog space for one simple reason: this blog is on our turf. This blog doesn’t exist on a platform which our posts will algorithm towards you, there aren’t links to it on billboards, and we have not yet deployed the worm which changes your homepage to reluekiss.com.
I just find it weird when someone tells me they want to be an e-celeb, and often times it’s through Twitch that they will find their fame. And when I ask them what people they watch on Twitch, they don’t seem that interested in the community. Small medium or large creators, not only can they not name the personalities of the genre they’re entering, they’re uninterested. They see streaming as something they’re doing unto the world in a vaccuum. Sure there are other streamers, but they are the competition. I once knew someone who was so serious in this venture that he would come into work and talk about his gang of 10 discord friends and what strategies they had come up with, how his “progress” (as if it’s 1-dimensional) is going, etc. One day he drops the hot knowledge to not stream the hottest, newest game, but instead to stream a game that has a moderate amount of viewership. In fact, a new release getting a lot of traction is good for you, because you can then go to some moderate or large category and ‘steal’ the that-game-only viewers.
While I expect every bit of this to be true, it forgets the reason why you probably wanted to stream in the first place: because you like it! I enjoy streaming my programming from time to time. I enjoy it a lot, but after about two hours is when the constant monologuing goes from fun to draining. I’m sure this is a muscle I could exercise if I were to stick to it once a week for some months, but the point is that I only ever streamed because I felt like talking in front of a mic. I enjoyed streaming, and I think I would enjoy it as a viewer, although it’s hard to watch programming streams because there’s so much context necessary to enjoy a programming stream. For some reason no one does 2-3 hour self-contained programming streams as well as Tsoding does.
One must be personally interested in the content they produce such that they would consume their own content if they were not the producer.
One glaring hole that I find is what version of myself am I targetting? If I were evil and wanted to make Cocomelon 2: The Rise of Roblox, I could tell myself that it’s awesome because four year-old Nate would have absolutely loved that movie. If I make an interesting tutorial on how to do modern web development on Crestron touchpanels without having a C# backend, a Nate from 6 months ago would love that video, but a Nate from 3 months ago wouldn’t have much interest in the subject. It’s just a bit convoluted with technical education because you can kinda just imagine an arbitrarily junior dev and say “that was literally me at X months/years ago”… because of course you were at some point maximally junior, compared to where you are now.
When I was first learning programming, I considered documenting whenever I had an “ah-ha!” moment, so I would have an accompying video for someone else who might have been stuck on what I was. Something I noticed was that so many of the beginner tutorials tell the same story the same way, such that if I’m not jiving with one person, swapping to another might not help that much. So, maybe other people learn like I do and would appreciate a beginner teaching another beginner. I imagine this is what university is like. The unfortunate thing, and I was aware of this at the time, is that the reason why everyone taught it the same was because they actually fully understand it. The biggest one that I remember that I could probably teach to a beginner is how HTMX works. That was really, really tricky for me to grasp for a few weeks.
So, it would seem that the Rule kind of devolves into “Only produce content that you like”. But maybe the Rule is more of a way to get yourself to remove your own biases towards yourself. As in, the Rule turns into “Evaluate your content on its merits whether it is good or not”. This way, the “Imagine yourself as the consumer of the content” is just a framing to get yourself to try to elimate some biases. I think it’s more of a grounding technique than it is a hard and fast Rule. Surely people write, act, edit, or otherwise produce content that they don’t personally find enjoyable. Is that really a crime? Would YouTube be better off if people only did things that they were personally interested in? On Twitch this question is a bit harder to ask because anyone who is in it for more than 6 months does it for the love of the game.
The opposite of the Rule would be “I know this content is not good enough for me, but it’s good enough for the rest of the audience”. Ignoring the Rule is egotistical because you value their time less than your own, and weird because you know that you’re producing slop. I like to believe the tech slop people just don’t know any better. They are the casual reddit andies who have heard of segfaults and tabs vs spaces, but when they say tech enthusiast, they really just mean average gamer. Yes I’m gatekeeping, and I think it’s high time. I have nothing against the normies, even the PC gamer normies who shout PCMR because of the ‘freedom’ a PC gives you over a Playstation, but still chooses to keep Windows with the OEM bloatware Corsair pre-installed for you. Those guys are fine, because they’re harmless. There’s no world in which they start issuing bad pull requests or showing up with their opinions on dev twitter because the only software they can comprehend creating themselves is in the programming language Unity.
The Rule definitely has a ‘there’ there, but you can also imagine an audience. I think book writers get the advice “Don’t write for everybody, write to somebody”. That phrase also is just trying to eliminate your biases when looking at a piece.
I love creating and I’m not going to stop any time soon. Every week I think about the youtube videos that I would enjoy making, every so often I think about a new blog post, and one day I still hope to operate a radio station with my beautiful wife. If we don’t get our license, maybe we just have https://reluekiss.com/radio

This website started with two goals:
• Post content.
• Allow Natalie to post content as easily as possible.
My beautiful wife isn’t interested in webdev, and I don’t want to force it on her. We both want to post content, and I want to it to be easy for myself as well, so the question is: what’s the right balance between making the typical blog post simple and out of the way, while maintaining the ability to reach out and make more interactive experiences, should they arise?
Astro is pretty far left in this category. The framework has grown a lot since I started using it (to include the introduction and eventual deprecation of AstroDB), and it is extensible because it is purposefully not a frontend framework, but a backend one. You can bring your own frontend framework and allow Astro to serve the static assets and pull in dynamic data via React or just normal JS or whatever.
There’s a critical flaw with Astro - the .astro file. It’s the thing that you look at first and say
“what the heck how would anyone find this acceptable?”, then in the middle of the bell curve you think
“oh, it’s actually pretty great because you can colocate your JS and markup kinda like JSX but as a
backend framework”, and then after a while you want to write your website using anything but the one
editor it works on and you run into wall after wall of either the LSP or the Treesitter not working.
I installed VSC*de on my home machine only because writing Astro was so atrocious on neovim. I don’t like tech that dictates what my editing experience is, and for Natalie that’s a non-starter. She will rather go without any of the dev tools then go down the web dev rabbit hole. Natalie has this idea of a semi-interactive story-telling thing (I’ll let her break ground on that because it’s really cool) and when she described it to me I just knew it wasn’t something that we were equipped to do. And I don’t want to pour more time into Astro when I could do it right and set ourselves up for success. No more happy paths, but instead our own path.
So, a few months ago, The Big Rewrite was underway. I went through a few iterations of what content creation would look like, but I’ll spare the details. For us, the blogs are the same. We write in a markdown(x) file in a folder, give some metadata details in the frontmatter, and the program spits out all of the html statically at “build” time.
Build time in what I’m uncreatively dubbing nat-ssg (library soon?), is just program initialization. Basically
the idea is that all of the data that can be pre-rendered SSG style will be pre-rendered. As in, there is a static
folder /var/www/reluekiss.com/public/ which the nginx server reads from. Whenever there a browser requests a route
that matches a file/filepath in that folder, we just serve the file.
I aboslutely love how we ended up actually rendering the files. There’s three ways right now:
• Write html and put it in pages/
• Write a blog post and put it in public/content/blog/ (I’m very careful with my filepaths)
• Write a templ component and manually put the url in the main.go
Blog basically works the same it did in Astro, only that I’ve unrooted much of what Astro did for us. We use
gomarkdown as all good Go web apps do, but we still have some heavy lifting to do. We never really liked
how code blocks worked on the website. We couldn’t figure out how to customize it on the server to have a nice
font, have lines on the left, make it centered, you get it. I think when we looked into it all we could find
were frontend JS things, and we’re very cautious of frontend JS. In Go, however,
we get some handy hooks to let us inject whatever we’d like when gomarkdown’s AST hits a code block.
Basically, it makes the AST, then as it renders it checks if that node (code block, paragraph, bold text, etc) has some non-default function it should use instead. Here’s what it looks like in action:
1package render
2
3import (
4 "io"
5
6 "github.com/gomarkdown/markdown"
7 "github.com/gomarkdown/markdown/ast"
8 mdhtml "github.com/gomarkdown/markdown/html"
9 "github.com/gomarkdown/markdown/parser"
10)
11
12func MarkdownRender(md []byte) []byte {
13 extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock | parser.MathJax
14 p := parser.NewWithExtensions(extensions)
15 doc := p.Parse(md)
16 opts := mdhtml.RendererOptions{
17 Flags: mdhtml.CommonFlags,
18 RenderNodeHook: mdRenderHooks,
19 }
20 renderer := mdhtml.NewRenderer(opts)
21 return markdown.Render(doc, renderer)
22}
23
24func mdRenderHooks(w io.Writer, node ast.Node, entering bool) (ast.WalkStatus, bool) {
25 if code, ok := node.(*ast.CodeBlock); ok { // Are we in a CodeBlock?
26 // Sweet. Let's grab out the language (code.Info) and the code (code.Literal) and give it to our rendering function
27 high, err := CodeHighlighter(string(code.Info), string(code.Literal))
28 if err != nil {
29 // If it errors out, that's ok! Just write out the original, it's not that deep. We'll catch it.
30 w.Write(code.Literal)
31 }
32 // Write the newly formatted code please!
33 w.Write([]byte(high))
34 // Tell the AST that we're good to go. No idea what the `true` is for.
35 return ast.GoToNext, true
36 }
37 // I didn't like how paragraphs were in the default rendering so I just make it a <br/>
38 if v, ok := node.(*ast.Paragraph); ok {
39 if v.Parent != nil { // Except if the paragraph is the first one.. that would look weird.
40 w.Write([]byte("<br/>"))
41 }
42 return ast.GoToNext, true
43 }
44 return ast.GoToNext, false
45}
I absolutely love how this turned out because I can actually dig into the renderer and change stuff I don’t like. That’s what you get when you step out of highly opinionated frameworks and do some Real Programming. No shots on those who like their frameworks. For work I’ve been developing on a React + Vite app, using a ton of shadcn and v0. It’s nice when what you’re making is the happy path and you don’t need to do hacks. But there’s a difference between a touchscreen that just needs to look pretty and be written somewhat efficiently and a labor of love which is this website. There are tons of these little moments through the rewrite and you can dig through it. It’s only about 2500 lines of the Gopher’s language.
Next, I want to show you how I did Components. This is what makes this a proper SSG framework, and will allow Natalie to dream big.
First, quick note on templ. I abosolutely love this library, I absolutely love the maintainer who gave this excellent talk that helped me understand not just how LSPs work, but why they have to work that way and the real-world nitty-gritty of getting something done. His explanation here is something I strive for whenever I’m explaining something technical to other technical people.
The library, as I talked about here is a breath of fresh air not just to Go, but of
the concept of server-side rendering HTML without server-side Javascript. No other HTML templating library can say with a straight
face that they use a Component model. That is, a function which returns HTML which itself can call other HTML rendering functions.
For some reason, these templating libraries expect that you’re writing one HTML file per route! Yes, you can have headers and footers,
(as Astro and now nat-ssg calls them: Layouts), but you cannot have an arbitrary piece of HTML encapuslate all of the child HTML that
it may need if you actually need to call code to render it. That sounds like a mouthfull, but if you’ve been trying and failing as hard
as I have to hate React, it’s the thing that I want to shout at the rooftops. Components! Why after a decade of React’s dominance in webdev
have templating engines not tried this yet? Have you seen jinja? Have you seen template/html? It’s absolute insane people who don’t
build interactive experiences on the web trying to make an API for building websites. You get bumpkis and that’s why server-side JS is
so prevalent. It’s impossible to write a good webapp without Typescript. Oh, did I mention that templ is typesafe and has an LSP?
And don’t worry about injection attacks because there is no shot you’re going to get
html in a component without running templ.Raw() (which is something that React does by default and it’s GOOD).
I am absolutely jazzed to see native having good DX. It’s a rare sight, and I haven’t built anything interactive with it so maybe I’m
missing something big but if you’re okay with your Typescript being in a different file, I don’t expect there to be anything to complain about.
We use Templ to build components, that’s its whole job right? Well excuse me because I wasn’t clear. These nat-ssg Components are called inside
of the html, then parsed + rendered during the “build” process. It’ll be clear with an example so here is our entire index.html:
1<!-- pages/index.html -->
2<!--
3<ascii>
4 _ _ _ __
5| \ | | | | / /
6| \| | __ _| |_ / /__
7| . ` |/ _` | __| / / _ \
8| |\ | (_| | |_ / / __/
9|_| \_|\__,_|\__/_/ \___| "We are Boingus"
10</ascii>
11<title>Nat/e</title>
12<description>We are boingus.</description>
13-->
14<div class="ml-[10%] mr-[10%]">
15 <ManyPostMini amount="all" author="all" sort="descending"></ManyPostMini>
16</div>
First, beacuse of the templ’s model, I put everything in a Layout. For the index.html, that’s layouts/Base.templ and everything inside of
index.html goes inside of { children... } of that layout.
Now that we have a decent start to the page, let’s bring in the content. <ManyPostMini> is a Component. It’s defined in render/components.go,
right alongside that CodeHighlighter from earlier (we reused the codeblocks for the markdown as a Component, how cool is that!). Now, every component
gets a struct, so here’s what we need to get going:
1type ComponentFunc func(component) (templ.Component, error)
2
3func registeredComponents() map[string]ComponentFunc {
4 return map[string]ComponentFunc{
5 "Code": code,
6 "ManyPostMini": manyPostMini,
7 }
8}
9
10type component struct {
11 Element string
12 Attributes map[string]string
13 Children string // any nested components are already html by the time their parent gets rendered
14}
First we need which Component we called in the html, that’s Element, then we get the Attributes as a map[string]string, and we also need to tell
Templ where to put its children because oh snap! It’s all Templ components the whole way down! Because know what Component (again, Element) we’re
trying to render, we can start writing functions that parse out those attributes, get the data we need, place the children where they should go,
and return a templ.Component to then be rendered during the “build” process. All of this was so tricky to get right which was the cause for a big
delay in the rewrite, but now that it’s here it’s so clear that this is how I want to write this website. Natalie can ask me for a feature request
on some Component that she would want that requires some third-party data, something from the filesystem, whatever, and I can provide her the data
she needs to do what she really wants to do, which is just write html and forget about the rest. And for my sake, I can write a new page with not
much overhead. And of the overhead I do need, I can make it into a Component!
I think this is an excellent strategy if you’re looking for a good balance between what you write in your static html and dynamic data.
Most of the rest of the rewrite was spent trying to get this portion to work.
Notable mentions include:
• failed attempt in squeezing all of our blogs that are on the filesystem into SQLite things asdf
• XML parsing from HTML comments to get page metadata so I don’t have to bake that in to the Go/Templ side
• ChatGPT for writing the HTML parsing for Components
• Natalie for doing really cool ascii art
• HTMX for powering out comment sections (we don’t do telemetry, so please leave one!)
• io.Writer for being such an excellect 10⁄10 5 star programming moment
Thanks for reading :)
nate

Opinions on phones these days feels like dream recountings. I will simply never care why someone prefers android vs ios. There is nothing anyone will ever say that would surprise me. Oh you like FaceTiming your dog? Wow… Oh you want to be able to make your homescreen look like the result of my 7th grade html class? Very cool!
I used to know a whole lot about phones. Every release from every major manufacturer, I would watch the MKBHD video, the LTT video if they did one, and I would brag to myself (I think this is called pride) that I could recognize any phone by its rear face. It wasn’t that hard a few years ago because there was less smartphones in use then. I think because post-2017 phones are actually good with decent cpus that means that there are more phones in circulation.
This is to say that I really enjoyed knowing about smartphones. I don’t enjoy consumer electronics content as much anymore. Knowing things is not a hobby, much less if the subject knowledge only exists to answer the question “What X should I buy?”. Headphone nerds you’re on watch too, by the way. Y’all only get away with it because to know about headphones you have to know about audio and to know about audio you need to know some kind of cool physics, electronics, and (probably minor) biology.
Since 2018, I’ve been whole hog into iOS. I don’t care about the ecosystem. My iCloud has been broken since 2021 because it says “not enough storage to backup” regardless of how little I ask it to back up. In fact I don’t think the ecosystem exists. I have a couple categories of apple ecosystem features:
a gimmick (Universal Clipboard)
something that exists just because apple restricts any other way of doing it (Sidecar)
something that should be an open standard, or use an open standard that already exists (AirDrop, iMessage/FaceTime, Health data)
something that is made completely obsolete by just using the filesystem (everything iCloud, AirDrop)
I also have the watch, which to be fair has some kind of argument for being a good ecosystem moment. When I set an alarm or get a notification, my watch vibrates in tandem. This is a big deal to me because I snooze a lot and I don’t like torturing my beautiful wife from her much needed rest. This is the only ecosystem feature that gives me pause for ditching my iPhone. Are there good watches that pair to androids? I remember LTT’s Linus would wear an apple watch without an iPhone in protest because although he hates, iPhones he hated the fact that there wasn’t an equivalent watch for android.
The reason I’m taking pause and looking left and right for a reason to switch to android is because of my newfound resentment for non-free software. It’s really, really bad actually.
I’m an AV integration software developer by trade. This means I’m working with devices like microphones,
cameras, displays, video matrix switchers, a bunch of random stuff from random manufacturers, some you
have heard of and some you haven’t. Each of these devices typically have an ascii protocol to talk to it
and that’s fine. Every manufacturer will make a different protocol for the various commands you can give
it, and that’s fine too. Standardizing the commands you can give to devices would be like standardizing
the food given to animals at the zoo. And some of them are tricky, sometimes there are variables you have
to embed within a command, typically just an identifier like 0x41 (ascii for 1), but other times it’
a bit worse like having a BCC where you XOR every byte in the message bytes, but not the header bytes
because those are always the same and not the footer bytes because that’s where the BCC goes! Like okay
sure, but with one device there wasn’t even an ID which means that every possible command and its corresponding
BCC is known ahead of time, so why not just include it in your docs?
Anyways, people have noticed this problem and make libraries for talking to devices. In this instance,
there is a proprietary programming ecosystem (Crestron’s Simpl Windows/+/#). The language itself is proprietary
and written 25 years ago, but that’s why they have Simpl# which is “just” a C# library. And what you might
do is use that C# library and compile it for use in Simpl Windows for the worse more traditional AV
programmers. The problem is, when people ship this they only distribute the compiled C#, no source.
This means that after a certain point, it’s locked and you can’t look into the library you’re using. Reminder the only thing this library is doing is building a string.
This was a nexus event because I am sitting there, on site, with the customer hovering over my shoulder as I’m trying to get a device to be sent the correct string command from the processor, and it just won’t send. I go into the module, goto definition a couple times, pull out a handy ctrl+f, and bam! Compiled C# library, do not pass go, do not collect $200. I’m sitting there dumbfounded why the function call isn’t doing what I’m expecting and I have to sit there and say “I don’t know and they won’t tell me”. It was kind of humiliating, like I’m the horrible programmer that introduced a mismatched state condition that makes the function not work. Maybe they pessimistically made it to not send unless some other condition is met? (I love OOP by the way)
I resolved it by grabbing the API docs and sending the string myself. This begs the question why was I using a library if the solution was so simple, but this is a post about phones!
I want to goto definition until I am at the top of the tree. This obviously doesn’t apply to operating systems as much because you have to be using the post-compiled version, but it still stands. I will never understand the inner workings of the linux kernel or much of the coreutils to have this aspiration pay off. But by golly it is worth pursuing!
Android is a soup of poor technical decisions (JVM) made 15 years ago and a dozen manufacturers gluing their proprietary nonsense on top. It is not an open source platform and it infuriates me when people think it is. If you’re using any major vendor, it is just as closed as iOS. And good luck disabling google play services. It’s just not tenable. If Google had GPL’d Android way back when we would be living in the space age by now, but in our reality it is just horrible.
Spotify and YouTube’s APKs are closed, I get that and I’m not against it. It’s a natural tension of commercial interests vs free software. But the OS being as technically and spiritually corrupted as it is, it’s just not an option for the serious person using their phone for serious work.
I want to be convinced that I’m wrong about android. I want to get a 10 year phone that runs a properly open source operating system, even if its UX is a bit worse than iOS. And to be completely real with you, I started this questioning phase when iOS 18 rolled out and completely nuked the Control Center [1]. Everyone complains about iOS 18’s Photos app, but the real victim is Control Center. A bastion of system controls that should be replicated everywhere has now been neutered by apple’s desire to appeal to the worst and most annoying criticism Android users (not iOS users!!) have been making: the lack of customization.
I liked how iOS tells you to go fuck yourself when you want to make gaps in your home page. I liked how custom app icons are up to the app developer to give out. I liked how control center was there to say “these are your system controls, changing audio source and wifi and such, and if you want some extra buttons like booting up Shazam or a timer shortcut, you can put it there and no where else”. This consistency is something to admire. It’s a restriction that allows less neurons to fire when you need a simple task done Anyone use uses vim bindings in their editor will understand what this means in practice. The buttons are there! Use it! If you want the button some place else, why? What value does this provide? It provides none and the detriment of adding buttons for the sake of it is tangible.
In my line of work, I set up a tablet for a user of the AV equipment to control the video/USB routing, camera controls, things like that. Imagine you’re a professor in a large auditorium and there are 6 displays What goes on them? What “mode” is the room in? What microphones are live? Some things we can assume with the 90% use-case of the room and some things we need input from the user to dictate what to do.
One of the most common requests frontend programmers will get is to add a button for X use case. Sure, you can do some action with five buttons but could you collapse that into one button? Please, I promise I just need a shortcut for the common use-case and there aren’t others that I haven’t considered. And it’s a matter of judgement if this is actually better. Much of my time in design is spent making the system intuitive. Many times these systems will be used by someone who is using it for the first time, and over the course of a decade, statistically, someone will use the system without being trained and without someone to help. This means that buttons can’t be scary. A button should not look as if it’s if it could be a destructive action without proper counterweights to prevent perceived destruction. Is there a modal that will pop up to prevent me from breaking everything? And if so, do I know that beforehand? This is mostly a solved problem in the web world and I’m excited to bring these insights when I’m building custom software for a client because they aren’t used to it.
But sometimes, a frontend programmer has to say no, you’re wrong and that the fact you have to do it my way is important. The only one of these new iOS features I think is worthwhile is the fact that you can take a photo as your lock screen and blur the background to a custom color. I use this feature to have a picture of my beautiful wife holding a jar of pickles as my lock screen while omitting the fact it was taken at Walmart.
This opinion of mine extends to desktop, by the way. For consistency’s sake I will specify that I don’t like how configurable linux is. I use dwm for my desktop environment (which means I don’t really have a DE, just a window manager), but only with much begrudging and using a slightly modified config.h by Natalie The customization on linux for me is really just adding features that should exist, like having dwm show the time with dwmblocks. If Windows had good UX like iOS does, I would be in this exact same dilemma but thankfully it’s shit all around so I don’t have to write a blog post about it.
I m asking for FOSS android options because I’m interested, though it’s a little bad faith to say that I want to switch. There is a list of features that I would require before switching that is probably not feasible.
First apple doesn’t track you like google play services does. One company is in the phone business and the other is in the advertising business. Natalie and I go back and forth on this, and I will absolutely admit that there is telemetry in iOS and perhaps some of it is personally identifiable. But with anything that google touches, that’s literally all they do. All of its data that is useful for its advertising business will always be personally identifiable. How could it not? It is always cringe to compare giga tech giant vs other giga tech giant like “my monopolistic company is better than yours!”, but in the issue of privacy there just IS a winner. And you can’t downgrade to a clamshell because the Chinese government is in the telcom networks. There has to be some degree of trust in the current market. There is the option of only giving your data to networks of open source or p2p softwares but that’s like the transition from twitter to blue sky. One is objectively better for the planet but it only works if the people you want to be on there, are.
Second, there are just so many UX wins on iOS that have nothing to do with ecosystem. I don’t ever have to worry about drain. I don’t have to worry about the 24gb of ram in my phone existing. I don’t have to worry about apps running in the background. In fact, I don’t ever have to close apps from the switcher thing. I’m sure this is a solved problem on android, but if so why do they require 6000mAh batteries to get 1.5 days of life? Why do they require 16gb of ram? Why do qualcomm cpus lag behind apple’s by 2-4 years? Why, in 2017 with the release of the iPhone X, did android copy iOS’s home bar in the worst way possible? Why does nothing feel consistent? Does android have good password management yet? If so that’s actually a huge win but I switched to an S21 when it released and my lastpass had to do an popup to insert passwords. As in, a modal like Facebook Messenger (another horrible UX disaster that people love for some reason). On iOS, it’s a feature of the keyboard.
Oh, that reminds me, a few years ago apple conceded and allowed custom keyboards on the platform. Have you ever seen someone use a non-default keyboard on iOS? No, because they are all just worse. There isn’t any magic to it, there’s no API that apple’s has that the others don’t. They are just worse hands down and yet another example of customization just being a pursuit of openness with zero benefit. I get apple’s attitude change, and I get why they want to give these little breadcrumbs so people tell their android friends “actually we can customize stuff”. The problem is that it will always fall short because until it’s something significant, like what android calls the launcher, or the notification screen, core parts of the frontend, it will always be less than on android and therefore fall flat to the imaginary person who has a problem.
I haven’t mentioned encrypted texting because of course there is RCS. But… oh, what’s this? End to end encryption isn’t a part of the RCS standard? The thing that every other messaging client, even Facebook Messenger, has figured out? I hate how the details don’t matter anymore. The RCS discourse has concluded now that Apple supports it (something they did begrudgingly, potentially because of the Chinese government, and way too late), but while that was going on it was extremely annoying that the assumption was that it was as secure as iMessage/WhatsApp/Signal. It does have TLS as a part of the spec, which is a start to prevent the Chinese from reading those texts. Why can’t the opposition get themselves together? Why can’t OSS and standards based solutions be as good as the closed ones? Oh no don’t tell me that the root cause is actually competition under capitalism…
Thanks for reading, this post was brought to you by a five hour flight in the middle seat by the worst airline I have ever flown. Natalie, I love you and I’ll be home in an hour or two (though I will have to post this after I get home… wait a second how to do I get this long of a note off of my phone and on my computer… why doesn’t iOS have a decent filesystem…)
[1] After writing this, I went to edit control center to get rid of the extra two pages they shipped by default. One was for your connections and the other was for media. These provide zero value because there is no difference between long pressing the wifi symbol to show that page, and the same for media. And it is worse because with multiple pages, swiping down means you go to the next page instead of closing the control center. Removing these erroneous pages brought back the close-by-swiping-up functionality that I have 6 years of muscle memory for. With the pages, you have to tap the outer bounds of the control center to close it. Just horrible. If I had thought to remove them before making this post I might not have made it, so thanks apple for having a major UX flub that inspired this post.
Image credit: https://amansinghblog.wordpress.com/2015/02/19/bad-user-experience-week-5/
This has been a big year for me. I got a new job, new place, and my beautiful wife is here in the US.
A part of this new job is making UIs in a WYSIWYG editor that was made 20 years ago with icon packs from 5 years ago. I think it compiles down to some kind of HTML but it’s impossible to tell because it’s all propietary garbage. It’s really difficult to make functional UIs in this because there’s no JS to control the behavior of the components - actually there’s not components at all. I might do an entire post about surviving Crestron’s VTPro, but that will be on my future A/V programming series.
I want to ramble and ramble about Crestron stuff but either a) my thoughts aren’t coherent enough to be in a well written blog post (I hold myself to a high standard!) or b) my thoughts are too well put together such that it’s a Code Tutorial and holy moly I don’t want to write another one of those it was really hard and I didn’t like it at all. Code stuff belongs in videos or books.
This post is just here to say hi. I haven’t felt like working on the site (go rewrite when??), and I haven’t had the time to do a full blog post. I’ve had a few ideas though, and I’ll try to push forward to actually write it. All of my ideas have been lost to me not writing them down…
I’d also like to have more specific posts. I kind of drift about sometimes or split the post in two. Seems like bad form.
Again, just saying hi (oh! and that I love my wife)
Have fun,
nathan
:)
Hey sweetie, you’re on the way here and I am very excited.
I hope you had a safe flight. I’m sitting in the international arrivals section of the airport with a ton of lovely people cheering on their loved ones as they finally see them go through the big double doors.
Airports are work, no one would ever want to hang out here, but we show up here anyways. We’re looking forward to seeing our family, coworkers, and not for the $6 bag of chips. How could it be anything else? Of course they have to be designed for brutal efficiency (though not land efficient). They try to makr the stay here acceptable, but no matter what it’s going to be work (especially for the traveller)
Airports are diverse. Every group of people use them and that’s apparent no where more than the international arrivals section. Every time I’m at the airport I am reminded that there is a very large number of people who use them every day whether I’m thinking about airports or not.
The screen tells me your flight is going through customs, and my eyes are wide open waiting for you to walk through the double doors.
I love you sweetie, I hope your stay here becomes to what we’ve imagined it to be.

This is a really long blog post about me learning web stuff in Go. If you’re not also learning web stuff in Go, this probably isn’t going to be interesting. If you are though, please leave a comment. :)
Once again, I wanted to make a simple document and I turned it into a blog post. This was meant to be a message in the readme of bear-hub, but it got too long and I don’t think a readme is the right place for a story, so I’m going to finish writing it here.
Also, if you’re looking for the website in question, it’s not hosted yet. I’ll put in instructions in the readme to run the server/develop it yourself if you’d like. In the future I’m going to put it on something like bear-hub.reluekiss.com.
Doing this made me think “what was the other post that I rambled so hard I turned it into a blog post?” and it was the post where I wanted to start making videos. Going through my posts, I saw my Abstractions Essay, which is about this exact project! I totally forgot I wrote that, so rereading it was a treat. Let’s compare four months later, six months into the project. I’ve learned a whole lot and have basically been doing this one thing the whole time. I’m okay with putting this much time into one project because I’m learning! And it’s still interesting! And hopefully I can reuse the code for a potential rewrite of this very website! Even if all of the text of the code goes in the garbage, the logic and what’s necessary to do a proper website is something I have absolutely loved exploring.
When I made bear-hub, originally called no-magic-stack, I wrote a list of goals. A roadmap. A list of things to aspire to. Well, after I made it I basically never looked at it again except to update what the project was doing and how to set it up. I’m going to rewrite the readme to be what it is in its current state and point to this post if anyone wants more insight to the history of the project (this will 100% just be for myself in the future, no-user-gang rise up). I feel like this project stuck to those goals very closely, actually which is nice. But, goals should only be written down if you actually care about revisiting them to make sure you don’t go off course.
Auth has been such a pain point. Now I have the JWT claims passing through the middlewares r.Context() correctly, so
I’m basically done except for all of the annoying parts. First there’s password resets, but then I want to do fancy
stuff like TOTP 2fa tokens, and after that oauth/passkeys, and then after that become an oauth provider. I think doing
that is the final boss. Maybe API keys after that? But that’s relatively harmless. I’m looking for ways to abstract
the auth package to the most reasonable degree. I want it to be as easy as the Auth.js/auth0 people, more on that later.
I absolutely hate how many articles and tutorials
there are online of software that does not pass the “am I, the author, willing to put this into a production service”
test. In with auth there should not be handwaves for this material. Either it’s
golden or it’s unusable, so tutorials that say “well in the real world you wouldn’t actually want to do this” makes their content useless.
Once I got up and running with htmx’s websocket extension, and some copypasta’d chatbot gorilla/websocket handlers, it was off to the races. Mostly everything since then has been frontend stuff. I originally had an issue with getting the form data to the server which took a couple days to figure out but after that I was smooth sailing. Recently, I made the chat interface look pretty because I thought that it wouldn’t be very encouraging to work on something that visually looks like dogwater even if it’s technically interesting. Eventually I’m going to have to figure out content moderation, which is more complicated than the current htmx websocket setup. Right now all it does is takes the div from the response and the response is an hx-swap-oob, so it just puts it in the dom. Say a moderator bans a user and deletes all of their chat messages. How does the client know to swap their messages with a “user has been banned” type of message? I think this requires some client-side js that I don’t have yet.
Postgres was a big pain point. I wanted to make it work so much, and it does work! Use it! Leverage your database to do work that is too annoying to do in the application. Want to auto generate a uuid? Easy! Want to do migrations? Supabase has a really great migration situation that I’m starting to miss already. I moved off of Postgres because I don’t want the database to do work for me. Supabase prides itself on It’s Just Postgres, might as well benefit from them doing all of the hard parts. But that, to me, seems like I’m learning a platform instead of learning a technology. Plus, with sqlite, it’s super duper local. Supabase has local development in the form of servers that live on docker containers that you can host on your machine, but I couldn’t ever get it to work without shutting off my internet. Anyways, all of this to say that I’m happy with sqlite because I want to do the heavy lifting myself, and now I get to learn the fun of hand-rolled migrations (or not, I haven’t gotten that far yet. I hear there are good tools for this).
What you’ll hear online is that htmx makes it really easy to send html from the server and render it in the correct place. This is true, but it’s not the whole story. When you have state in your application, you have to keep track of it somehow. What htmx really, really wants you to do is to move almost all of the logic to the server instead of the client. Whenever the client wants a page, you make a new state given the cookies, query params, headers, form submission fields, etc, and you render a new html document to send to them. Importantly, all of the things I just listed are browser/http primitives that are here to stay. You leverage what browsers do best so your server can do what it does best: work. Once I unlocked this idea of keeping having a ClientState/AuthState struct, and being able to generate it based on what the client gave, it really helped. You don’t need to negotiate with the client of what it knows. It knows nothing. I saw someone online say (paraphrasing) “A browser doesn’t need to know how a calendar works. It only needs to know how to render what the server gives it. The logic of the calendar is on the server, the representation of the calendar - that’s what goes to the client”. And I agree with this so much. The fact that javascript has gotten so good has been bad, because it means that the browser does more work than it was ever meant to. Stop making calendars on the browser, that’s not what they were made for. Keeping business logic away from the browser - that’s what htmx is good at.
Contrary to what I just said, I’ve thought about doing some SPA-level stuff with htmx. The idea is instead of doing
traditional routing where on a GET /profile/yogibear61 request, the Go server responds with a fully complete html document, what really
happens is I render, say, just the <body> and htmx hoists the response into the body.
This way there’s not a full page reload with a potential flash of unstyled content.
The header, footer, etc, continues to exist, they aren’t destroyed, it’s just that the <body> tag gets
replaced. This I think would help make the page feel faster and whatnot, but I’m not sure how legal that is in my mind
yet.
Quick note on something I said in my Abstractions Essay. Htmx does have some security implications, but only
if you have massive skill issues. You do have to think about sanitizing user input from html, because if you’re
telling the browser “render this div”, and that div contains a chat message that says <script>alert("pwned")</script>,
and it actually runs, yeah you have some security implications. Just sanitize your inputs, on our blog site we do this
with the comments and it’s literally one function.
Sqlc is really, really good. Upon the transition from postgres to sqlite3, I tried to do the classic abstraction
thing where the application calls my db package, and that db package wraps both the sqlite3 and postgres
calls. The application doesn’t know whether it’s using postgres or sqlite3, it just gives the data to db,
and db figures out what to do with this. The problem otherwise is that postgres and sqlite3 have different types.
For example, postgres has a proper uuid type. This means that sqlc gives that field a uuid.UUID type. But sqlite3
does not have such a type, it only has text. So, my db package would wrap both of these calls and the application
would give whatever is most convenient (in this case, a uuid.New()) and db would disperse this information to
what’s correct for the database. The problem with this is the synchronization and return types. Basically, the only
way to make it all work is if I made everything in postgres whatever sqlite3 can handle, and from which database
should I return? What if I was writing to them both at the same time? This issue is solvable, but as I was doing it
I realized it required an amount of effort that wasn’t worth it. If you’re curious, you can go
here
to figure out how I was trying to crack this egg. It is not pretty, but you’re going to be looking in src/db
Templ is such an insane bit. The tooling and compilation has only gotten better since I started using it. I have one merged documentation PR and another PR which I won’t link that is a rewrite of a static site generator someone wrote for templ.guide. I absolutely love the project. It brings React’s functional component model to Go. That’s it, that’s all you need to know. If you’ve ever tried to use Python’s Jinja or Go’s tmpl for html templating, it’s an insane breath of fresh air. You literally pass Go structs, slices, whatever you want and as long as it goes into a string before it gets rendered, you’re golden. The PR I linked was documentation on how to get tailwind’s LSP, html’s LSP, htmx’s LSP, and templ’s LSP all working within Neovim in .templ files. The fact that that works is insane, and it’s getting better and better. Templ doesn’t have Hooks, or RSCs, or any other real React primitives, so comparing the two is a little bit disingenuous. It’s not a React replacement, it’s an html templating replacement. If you’re someone who uses React for html templating, you are just simply doing it wrong. There’s no need to bring in all of React for this job, and that’s why templ and htmx work so well together. You have the templating you need, and the htmx to do the client side shenanigans. State? Like I said before, the client holds no state other than the tools that are built into http. useEffect? Wake up darli… what are you saying? Rendering lifecycles? Hydration errors? Do you know what year this is? You’re speaking nonsense.
Like I mentioned before, I want to make a package for auth in Go web apps. But it’s not easy. If you look throughout
the code that calls on the stuff from src/auth, it’s not as simple as taking on auth from on high. For example, I have it
so the SignUp/SignIn struct has a method of signature RenderErrs() []string so when I’m rendering the html, I pass that
struct into the templ templating, and within the template if there is anything within any of the errors, it will show them
in little boxes on the page. If SignUp.UsernameErr exists, it will make the border color of the username box red. How could
you make that into a package if you didn’t own or understand the contents of the struct? The jwt boilerplate I think is
production-ready, so that will probably be the first part in my auth series, but that one file alone probably isn’t worth
installing. Just copy it! If you look at it and you think it’s good, copy it now! I like it, and if you don’t then
please cut an issue because I want to get a second opinion. One cool gimmick I’m going to pull with my auth series is that
when I feel comfortable enough to share it, I’m going to pay real cash-money to pay a web security professional to do a code
tour and pentest on my auth system. I want to get real insight on what I personally overlooked, and what common mistakes are.
This step is important because the number one reason I hear online of why someone should never, ever roll their own auth is
because there are “so many” footguns you can find yourself in, you should really leave auth to people who dedicate their
entire company towards security. But, to me, if you own your auth, and it Just Works, then you have it forever. You can
microservice the heck out of it, and as long as you don’t have severe skill issues, it will still Just Work. Auth is a concept,
so if you have it done correctly then it will Just Work forever. That is, unless passwords really become out of fashion
and you have to move to passkeys, but I’ll have that covered to!
I’m more excited for this project that I was working on playlist-powertools, a webapp that at least had a potential of having more than one user, and I was newer at programming then! Learning concepts instead of frameworks has been a lot of fun. I do want to say, however, that the chat service is something I intend on making a real thing that people use. The tagline: “A self-hostable twitch chat clone”. I originally thought of making the chat backend be an irc server, but translating between irc and http (which I would need to do to avoid using a javascript irc client) seemed unnecessary. I’ll see where that part of the project goes.
Thanks for reading. I didn’t want this to be my longest blog post yet, and here we are. I get really excited with this stuff because now I have multiple avenues of motivation, and this website is one of them.

Watch this video of Bill Gates talk with David Letterman in 1995. Letterman wants to be sold on this whole “internet” business and, I believe, is earnestly listening. Without any data to back this up, I think he’s voicing the concerns of the unconvinced majority of Americans who constantly hear about how the internet is going to change the world, but can’t find a way to fit a computer into their daily life. Gates makes a joke about this, that Letterman “has too many assistants” for the internet to be useful. Media diet? Who is going to take someone seriously when they say “imagine all of the new media you’re going to be able to consume with this technology!” Has anyone at any point of time wanted more media to consume? That seems like a weird sell, even for someone in 1995. Productivity could be a proposition, but if you’re not working in something that a computer could obviously fill a void, the demand isn’t there yet.
This is where AI people find themselves now, and where crypto people were a few years ago (before everyone gave up trying to make a use-case that doesn’t exist). You simply cannot sell a dream. Bill Gates didn’t convince Letterman in that interview, and nor should he have. AI people will look at this conversation and think “How silly is David Letterman in this video! How could he not see the future of the internet? It was 1995! It was already there, just waiting for the likes of Amazon and Netflix to change the American economy forever!” In reality, though, that is the only reasonable opinion. If you don’t see a value add for a technology, it’s not on you to “understand” it more, it’s on the technology to make it relevant to you if they want your adoption.
AI people are stuck right now with this reality. And what’s worse is that they have very little to show for their fruits. ChatGPT is a great technology and has been incredibly useful for me personally learning all this programming stuff. If I were to add up the amount of time the chatbot has saved by giving me good analysis on specific concepts and tutorials, and subtract the amount of time I’ve wasted by investigating its hallucinations, it’s undoubtedly a net positive. My favorite thing is to have it explain “like I’m coming from Python”, or something to that effect, so its explanation is tailored to what I already know. This tool is not something that is easily replaced by reading the documentation because of this tailoring. I got so much use out of the chatbot that I paid the $20/month since GPT-4 came out up until a couple months ago when my skill issues subsided enough that I could deal with the free tier.
I bring that up to say that I’m not an AI doubter in the sense that my friend was, who, when she learned that GPT-4 got a passing grade on the BAR exam, said “So what, if I had access to the entire internet during a test I could pass it too”. This is the threshold I think some of the early AI people were trying to convince us passed. It’s not just that the thing can do X provided much of the information from the entire internet. It’s that.. holy moly! We just found a way to wrangle much of the knowledge from the internet in one program! I see a future where this is much more useful than it is now. Maybe the chatbot can gather insights into the information that we gave it that no human would look for. Maybe the machine learning black-box part actually makes it understand the underlying data instead of being “a very fancy auto-predict” (I hate this response the most). Unlocking this could be huge.
Crypto is the same way. I imagine crypto people 10 years ago were yelling at their Lettermans who would say “What do you mean I have this digital money? I already have real money that people actually care about. Even if places would take this digital money, the fact that it’s so volatile doesn’t make it a useful currency”. After 15 years, that argument basically won. After the NFT boom and bust, crypto is no where to be found except for places where you don’t want big man watching. Here’s the crypto sell I say to the Lettermans in my life: It’s not about money, or about randomly generated pictures of monkeys, it’s about the fact that we figured out how to have scarcity exist on the internet. Without crypto, any 1’s and 0’s you send can be infinitely replicated or transformed and there’s no sense of ownership except by the legal system. With crypto, there is now a concept of digital ownership. Is that useful? Could the title to your house be put on the blockchain to circumvent the banking system with their fees and monopoly on accreditation? We don’t know, we’ve looked far and wide and it hasn’t worked out to be useful enough for adoption in any public setting. But, it’s cool.
As I said, no one is trying to sell crypto anymore. The hype is gone, the refs have decided it’s a bust. No hope in the foreseeable future except if bitcoin hits 6 digits and the eighth round of scam artists come out of the woodworks. I don’t think there’s any way that AI will fulfill its lofty promises. OpenAI has one really good product. One product, a new internet does not make. We’ve gotten so used to platforms and opaque software that we seem to have forgotten that the internet was built on so much ridiculously open-source software it would make Sam Altman of “Open”AI’s head spin. The industry has forgotten this fact and it’s very sad. I have no problem with a company making software, not open-sourcing it, and making a butt load of money off of it. Pop off, make your bag, have fun. But if you’re trying to enter the next era of computing off of something as proprietary as Facebook or Fortnite, it’s not going to work.
(speaking of, if anyone has some insights on why open-source never made its way into gaming related software until things like Godot, please leave a comment).
Not only do these LLMs not have standards to be graded off of, you kind of can’t! They aren’t deterministic, it is impossible to get the same output with the same input. They are rushing to build a new internet and they have no idea what it is. They are trying to sell Letterman, but when Letterman asks “But what is it really?”, they give wild speculation that requires technology that doesn’t exist yet. “But we’ve come so far in just a few years, surely we’ll catch up to our self-imposed expectations!” Why set them in the first place? Why are you doing this to yourselves? I thought VC money was expensive these days. They rushing to push LLMs to Lettermans but this time, there is no value. There is an annoying pop-up on Google that brings down the results of your search with LLM garbage. Every time I’ve seen an “AI Overview” on Google, it is bare minimum worse than the first link. And how could it not? Automatically running a GPT-3.5-quality query on some significant number of Google searches would be so much compute. Maybe they have enough hardware, but then it’s an energy question. Those LLM queries aren’t cheap compared to a typical search. You might notice the lack of graphs and statistics, but I don’t see how I’m wrong. All I’ll say is that they either 100x the quality of their LLMs, or they will recoil in embarrassment, subject to clowning for the next decade on how awful this era of Google is.
Would Google search be better as a conversation? Again, no statistics, but some huge percentage of Google searches are not complicated enough for an LLM to be useful. If I want to order takeout, go to my movie theater’s website, go out and do anything planning, I Google the company. Most times, I could have just gone to “texasroadhouse.com”, but instead of taking the risk of going to a domain squatter who takes advantage of this naivete, I’ll Google it and click on the top link. And sometimes I’ll do this even for websites that I’ve visited a hundred times. A Google search is so cheap in my mind that it’s not a problem to do an extra two clicks for a guarantee of correctness. The Gates of the world might say “Well you’re used to using Google in that way because of its limited functionality, when it can do more you will do more with it”. And I agree! Like I said, I paid OpenAI $20/month for around a year, I’m well aware of the potential value add… for complicated questions that require a chat experience. These AI Overviews shouldn’t be in Google by default because most of the time I don’t want an overview - I want a resource. And there’s no amount of algorithm shenanigans that will be able to infer what I want. I don’t go to ChatGPT for the height of the Empire State because it’s not the right tool for the job. Google should have a separate area that doesn’t distract from links for this task. I imagine Google Bard is a good chatbot, I haven’t tried it out because I doubt its free tier is better than GPT-3.5, but that’s the appropiate area for a chatbot to exist. It’s own space. I’m not sure how Bing settled this. Did they distract from their links with AI stuff? Did it make a chatbot query and interrupted the results with whatever it said? I’m not sure, I never used Bing in a serious enough way, even after the initial hype, to remember.
The first question (put last in this rambly post) is “Do we want this in the most idealized form?” Not to be a Letterman, but I can’t understand how talking to a computer in natural language is going to be better than what we’ve devised in the last 40 years of user interface design. Obviously I won’t use a chatbot to interface with my computer, I like the least amount of things in between me and what I want my computer to do as possible. But take someone who wants to not manage their windows, not manage settings, their filesystem, they just want their computer to work. Maybe they use their computer in the bare-minimum case: content consumption. Would consuming content be better without using a mouse or keyboard? How would you correct errors from the AI? Talk to it again? What about when the AI understood what you said, but not what you meant? And mind you, these aren’t searches for internet content, this is basic manipulation of your computer. These keyboards are faster than speaking because they are buttons! Real deal, tactile, do-something-instantly buttons! Does anyone walk around with laptops where the keyboard is a touch screen like your phone is? No! Because that’s awful! Do people who write for a living do so with a voice-to-text prompt? No! Even if they had an editor by their side helping the computerized scribe correct errors, the writer would still be frustrated because they now have something in between what they want to do and what they are doing. It’s an unnecessary complication. Unless of course, it is necessary. Accessibility could be a big win for AI - from transcribing poorly written websites & software into something that you can understand, or by being exactly what Sam Altman wants everyone to do in talking to their computers in natural language. That would be a great win for these people! But no, we aren’t satisfied with an incredibly complicated LLM like GPT-4 that makes bajillion dollars a year, or for finally making most software accessible to people who have been overlooked by developers, or with giving people links to the resources they were asking for. We have to make a new internet, off of a platform that no one understands, no outsiders can contribute to, and without a faint definition of what data comprises these LLMs.
This was my AI rant, hopefully I have expressed enough opinions to either eat them in 5 years or be a vindicated gigabrain.
Footnote ramble:
A month and no post. Not the best. I’ve been busy! I have a new job situation that I’ve had to spend a lot more time at than I’ve been used to, I’ve been working on my side projects, and I’m just simply afraid getting out of my step with writing compounds itself. No more! A post, to get back into things.
A question: Why does everyone hate their neighbors?
It’s something I’ve noticed, where, in a large organization, people don’t like the other shifts when talking among their shift. And if they are talking to the other shift, well they don’t like the people that work in the other office. And if they’re talking to the people in the other office, then they don’t like the people in the other building. “Why would someone write this document this way? It’s horrendous!” a worker says to their roaring companions. Then, the next day, when the author is in the room to explain themselves, it’s all “Oh, right yeah I’m sure there was something about that”, apologies, and “no problem”s flying around. It’s insincere, and it makes me question whether my friends say the same thing about my when I change shifts, or offices, or buildings.
Hey Natalie, this was a lot of fun to write. Please don’t let me wait another month to write again :)

This post is about my experience with dreams - I’ll try to keep my dream anecdotes to a minimum because let’s face it - other people’s dreams are not interesting. Towards the end I’ll put a website update (a lot of little things!)
Active dreaming was a superpower when I was a child. I could fly, manipulate things, talk to people, work things out, ignore my problems. It was a proper escape that I had a good chance of achieving in any given week or so. I remember a dream where my alarm was going off while I was in my old home in South Carolina. Desperate to not miss school, I figured out how to concentrate to just so slightly move my real-life shoulder and make it to the bus on time. This skill would prove immesurably useful for the rest of my life. The ability to exit a dream whenever I figured out that I was in one would save me from countless hours of nightmares and generally boring dreams.
Do you ever have boring dreams? Usually it happens in an instant - so quick that it’s very much possible that I was going to wake up anyways and saying “I’m bored” was my brain’s convinient excuse for waking up prematurely. I’ve woken up laughing at what just happened in my dreams. Sometimes my dream makes an argument at me. Like a meditation state where my psyche wanted me to consider something that an awake, sober brain would have never thought of. About twice a month, I get sleep paralysis. Data on this is a bit fudged because I deliberately stopped keeping track of my dreams, good or bad. There was a long time where the bad outweighed the good.
As a kid, I remember Vsauce and the rest of the science youtube people, as well as my friends at school talk about lucid dreaming. I never really perfected the on-demand lucid dream, but I had plenty of them without any effort - it seemed a waste of time to try. Although, remembering dreams was something I cared about. There was so much life in them, so I would write them down to remember them. You forget your dreams because your brain doesn’t find them worthwhile - well, if you write them down and make an exercise to remember them, your brain says “Oh, this is important to us, alright let’s not dump it 5 minutes after waking up”.
So, that’s what I did for awhile. I don’t remember how I kept track of them, but I remember making an effort to write them down in my phone or a notepad whenever an okay one rolled around.
Then, the nightmares started to creep up more and more. Or, not even nightmares, but a complicated drama that would make me wake up in a tissy. I didn’t like these so I made an effort to forget the dreams - no more logging them, no more analysis, no more stress. This has worked for the most part and is why I say that dreams are overrated. I’ve found that I sleep better when I wake up unencumbered by the story that unfolded during the night.
The paralysis part I don’t think changed that much, but it certainly helps when the scope is smaller. Most of my paralysis dreams are without any sight, just a voice and some explanation as to why I can’t move. I got these when I was a child too. Most traumatically, the narrator in my dream would lock my eyes onto some object - something that is innocent but telepathically tells me how evil it is, how it wants to do this and that. Obviously you don’t control yourself in most dreams, but to be told under no uncertain terms that you can’t move or look away from something, with an accompanying sense of doom, it’s just too much. There is probably a normal distribution of active dreaming, where in the middle is most people and they’re fine, and the people on the left that wonder what all the fuss is about. I’m convinced I’m on the right side of that distribution - more dreams and more dramatic dreams than most people.
I would happily go to the lesser-dream side of the spectrum any day of the week. Sleep is for sleep, and the best part about sleep is feeling well rested. Even my most fun dreams were only fun for less than an hour after I wake up. After that, I go about my day and life my life. Dreams don’t help me in any way, except for one.
The dream that had an argument at me was when I was deciding on how much my family meant to me. I hadn’t talked to my family in awhile, I had ongoing fights with some, contempt for others, and I was considering going cold turkey on them. This dream showed me a nice day with my extended family, and said “This is what you can lose if you don’t keep up with your relationships. You can’t expect everyone to hang by your side after you allow the relationships to wither away.” That’s something I’m grateful for, but boy is there so much bad stuff. I was almost late to work just the other day because I had four hours to sleep (skill issue on my part, for sure), and I had a nightmare in the middle of it. After a nightmare, I’ll just distract myself with some media until I’m no longer scared of the nightmare continuing, but it’s a bit of time and lost precious REM.
The paralysis dreams are, like I said, about twice a month, but they don’t bother me too much. Many of the paralysis dreams are fine? They don’t bother me, I just relax like a chinese finger trap and wake up. The more you freak out during one the more anxious you become, making the whole situation worse.
If you’re someone who has frequent nightmares, consider the fact that alcohol consumption makes nightmares much more common. Alcohol is awful for you in so many ways, and sleep quality is one of them. I don’t drink very often, and drunk dreams are iirc kinda fun for me, but maximizing dreams is the last thing in the world I care about.
When I get into bed, I just want some rest. No games, no flying, I want to wake up refreshed and ready to tackle the day.
The site now has a couple new pages, an updated (read: stolen from Natalie’s side) header, and the colors on the home page are cooler now. The best colors for the home page are grey and a different shade of grey, but doing that looks kinda boring so we’re not doing that. There is the irc page for showing you how to get to the IRC server, and the js page for telling you where in our site has JS and what disabling JS would mean for you. On the tv page, Natalie figured out how to get the HLS stream’s TLS certs to be properly signed, so there’s no question on the authenticity of the stream you see. The site is open source still, at the updated /gh/ tag in the header, so give a looksy and tell me what you think. We did a lot of refactoring today to make the layouts easier to work with and more Natalie-proof (love you sweetie). Also the Meta.astro was fixed so that when you link it in discord or twitter or whereever, it showes a nice thumbnail if it’s a non-blog post (it looks so cool! I’m really happy that Natalie did that it’s really cool looking and she’s really smart and I love her a lot a lot), as well as the title and description of what’s going on. I wanted to add a checkbox where if you clicked it, it would hide the content on the page so you could appreciate Natalie’s really great choice in backgrounds, but I couldn’t get it to work. Anyways, I hope you like our little site :)

This post is a collection of thoughts I’ve had about tech youtube for the past couple years. At the bottom there is a website update, if you’re interested.
Every time something novel releases and we turn to the tech influencers for their expertise on consumer electronics, they read off press releases and give benchmarks without a story. MKBHD’s video on OpenAI’s Sora, he doesn’t actually say anything. No hard opinions or facts, he gives the audience the same clips everyone else is playing (along with some other clips from Sam Altman), and not much for commentary. Yes, it’s conflicting that a youtuber is showing off a technology that could make his job more competitive, and he somewhat addresses that but never in a serious way. There is this pseudo-intellectualism from youtubers sometimes where they try to sound educated and nuanced, but when you think about what they’ve said, it’s just questions without any contribution to the overall narrative. A smaller problem is that every video/article has to establish the facts about the product in case this is the first time someone is hearing about the thing. They have to set it up for people who aren’t chronically on twitter, but after all of the boilerplate, surely different media outlets will do something unique right? They will provide some insight that the others didn’t catch, maybe a novel use-case or perhaps some hot takes. This is what’s missing, and that’s what I mean by mojo. I want youtubers to have stakes again. I want to see a wet Linus walking down a Japanese street again.
Maybe the content hasn’t gotten worse, but I’ve gotten better. Over the past two years, I’ve gone from knowing a lot about consumer electronics to learning about software. At first, it makes sense to draw a line between the products I see and use and the software that powers them, but unfortunately no one makes content on this front. You can’t make content about this because most of the time the software isn’t open source. There’s nothing to talk about except for using the products and the landscape the product falls into. This is where tech youtube is still very valuable to me. When I hear about a new pair of headphones, a laptop, or a VR headset, a well written review will compare the gadget with its competitors and I can get a grasp of the bigger picture - nothing exists in a vacuum. It’s almost like I outgrew the consumer electronics content I used to enjoy. I would watch a review on a keyboard or a graphics card with no intention of ever buying it but I just wanted to know what’s going on.
But more and more lately, I’ll watch a review and straight up not learn anything. Sure, the phone exists and it has some specs, some ups, some downs, but the picture is complete. For phones, I give a pass because the market itself is so extremely uninteresting for anything I care about. There is fierce competition in the $300-$600 price range, but above that I’m just not going to care and that’s fine. Companies figured out phones for the most part, the fact that each generation is incremental is… fine. I don’t like it, but until companies start making interesting products, I don’t expect the content around them to be interesting (again, for phones specifically). However, in my next phone purchase I will be looking to put an open source OS on it, like GrapheneOS, so I’ll be looking out for that, but that’s not going to be until my iPhone 13 Pro takes a dive. As much as I (now, thanks in part to Natalie) have a disdain for Apple’s business practices, they make really good products that I will probably still recommend to people. When M1 came out, that was big and content around it was alive. People were excited again in a way that I wish we could maintain in a slow cycle.
Take 1: Creators are only as good as the products they have in front of them. The media is a reflection of their audience so if we are going to be bored, they are going to be bored. It follows, then, that when there are boring products there will be boring content. If a creator only shows benchmarks and gives a general guide on whether a product is a good/okay or an obviously bad buy, then that’s indicative of a company doing very little to excite their to-be customers.
Take 2: The inability for creators to make a “sum greater than the parts” of the products they have in front of them demonstrates that they are lazy parasites who only do well when thousands of engineers invent a new toy. This demonstrates that the value add of creators is so little that you are better off reading press releases and spec sheets yourself.
After spending years watching consumer electronics content, I now have the muscles where I can see some benchmarks, compare the products myself and come out with an idea of what graphics card my friend should buy, or what laptop I recommend to my family member. Again, I got these muscles from these youtubers and I just hate that I’ve outgrown them. I know they can provide more insight than what I have because that’s their job! They spend the most amount of time on these things, and I don’t see how it’s possible that my intuitions are on the same level as their research… unless, of course, they aren’t doing much research at all.
Sorry I haven’t provided a lot of specifics, of course this is a general, nebulous, vibe of the content. If I were taking this post more seriously I would provide some proper evidence. I will say that I stopped watching LTT and told my youtube algorithm to stop recommending all of the LMG channels because there was a period of a couple months where I didn’t learn a single thing from any of their videos. Months! I would watch all of their videos (except for the quirky cooling videos, those were always boring and for physics nerds), even through the controversies. I wish LTT as a brand didn’t water itself down to the degree that they did. Developer content has been more of what I enjoy. Software is a rabbit hole where to be an expert in even the surface level fields will take multiple lifetimes (Rust joke here). It’s a treasure trove of interesting technologies and history of those technologies (see: X11, LLVM, and yes, web dev) that are currently being iterated on. We haven’t learned what makes the best programming language because there isn’t one. There are players currently rewriting the book on something as simple as “How do you make a mobile app?” with the new React Native, or new programming languages like Rust and more recently Gleam, or new ways to think about your operating system configuration with NixOS (potential Nix arc inbound). Above all, this hobby is more interested in creation.
House M.D., Season 3 Episode 1:
Dr. Wilson: The fifth level of happiness involves creation, changing lives.
Dr. House: The sixth level is heroin, the seventh level is you going away.
If your “hobby” revolves entirely around consumption, then it isn’t really a hobby. It’s a grocery list. Likewise, being an expert on these things is good for being the tech wizard of your friend group or your family, but, to go even further, having opinions is not a real hobby. Especially if those opinions are those that you copied - at that point it’s literally just a list of things you know.
This is why I watch so much of Theo and ThePrimeagen. They made a space on social media for senior developers to talk about senior developer topics. You don’t see them making tutorials on the difference between let and var in Javascript - you see them talk about best practices from their professional experience. Prime does something like tutorials, but when he does it it’s in a classroom with in-person participants, a projector, and a script. But the bulk of his content is talking about the industry, by way of going back and forth with Twitch chat and reaction content of other people talking about the industry. This content is much less about my desire to create and more about my interest in their world. In this space that they’ve made, I get a glimpse of a world of countless software engineers who go to work to create. And these people aren’t selling anything, they talk so much about their jobs because they have passion. I like hearing from passionate people.
I would like to mention homelab youtube. This gigantic corner of youtube also has passionate people who are just nerds. They like setting up their servers with their Home Assistant, their Plex boxes, and whatever else, because they enjoy integrating technology into their everyday lives using open source software. However, every time I look into these people’s content, it’s a) always some kind of tutorial that I’m not interested in doing right now, b) talking about some freemium SaaS that I ask myself “isn’t the point of all of this work to get rid of those guys?”, or c) an exploration into a use-case that I could never see myself doing. No, I don’t want my coffee maker, window curtains, and lighting to automagically do its thing when I wake up. I don’t like tech like that. Keep it separate - my tech lives there and I can walk away from it when I want. Yes, my coffee maker has a semiconductor in it, but it sure as hell isn’t going to have an app.
And again, that’s why I’m disappointed in what MKBHD and LTT have become. It’s like they read so many Verge articles that they forgot that they are not the Verge. They are allowed to have stakes and hot takes. The whole point of creator media is that you can cut through the nonsense and say something authentic.
PS.
The website has comments now. This was the last feature that I cared
about adding. If you’re interested in how I made comments work on an SSG
site in Astro, you can see the source in the /gh/ link at
the top of the page. Natalie wants to make it so you can do
“>>number” to reference a previous comment, and I’ll get around to
that at some point. As well as paginating the comments so if there’s
more than X, there will be a Page 2 with another X number of
comments.
I don’t think I’m going to rewrite the site in Go. Astro’s templating language is just so, so good. Even templ as good as it is, in a language much better than Javascript, isn’t nearly as good as what Astro has built (ironically, the Astro compiler is built in Go). I started to mess around with porting over the components we made. I’m willing to put in the work to make it happen, but in the best case scenario it’s just worse in every way. I’m trying to think of ways to bring the wins from Astro into Go - like literally what the ideal solution would be, all skill issues be damned, and I just can’t even think of what would be better, especially with the constraint of no new file types. I have a PR where I rewrote someone else’s PR for making templ’s documentation website templ.guide using templ (right now it uses docusaurus, an SSG framework made by facebook using javascript). I know how to do it, I just can’t justify it for our site. Component composition in Astro is good, the tooling is good, and at the end of the day, this site is still SSG. The only server that exists on the site is the endpoint I had to make for posting/getting comments. This does mean, however, that the site uses htmx for a) getting the comments on pageload and putting it in the dom and b) sending an error message in case of an internal server error. This does break our promise of “no javascript unless specified otherwise”. I don’t know how to specify that the site uses this library in a way that makes sense for the site. Maybe we’ll have a javascript notice with an explanation at the top. Natalie, this is something you care about more than I do, so tell me what you want that to look like. Or, I’ll resolve some skill issues and handroll the javascript necessary to get the comments. There is no way around javascript unless we build the entire page on each page load through server-side rendering. In my opinion, having a Node server building every page on load for every request… for a blog site is a bit more egregious than some client-side JS to request the 1% of the site that is dynamic. Again, Natalie, tell me what you think and I’ll do it.
In any case, it is nice that (besides the earlier feature of mentioning previous comments), this site is Completed. Natalie can do the quirky things that I love her for doing with the site, she knows how to do it, and I can rant about stuff in a way that really brings me joy.

I read (listened to) The Fourth Turning a few years ago and it is such a compelling narrative that I wanted to believe it. It gives this grandiose vision of American history. Basically, it says that society exists in one of four turnings in a cycle: a high, an awakening, an unraveling, and a crisis. We move through each of these approximately every 20 years, and at the time the book was written (1996), we were on the tail end of the unraveling, and so the authors predicted a crisis turning was up next. They went as far as to try to predict some of the things that might happen to usher a crisis.
This isn’t a book summary, again I listened to the audiobook like five years ago and I don’t recall the details. I just had a thought earlier that I’m not sure if the way we think about generations fits anymore, if ever at all. I remember when I was in middle school, Vsauce did a video on generations and mentioned The Four Turnings book. He clarified that it’s not just criticized, it’s also unverifyable. I remembered him mentioning the book when I was on a big audiobook spree and actually listened to it. Again, it’s mesmerizing. “We’ve been though worse, we’ve been through many crises, this one is not so different. Maybe we can also get through climate change.” It gives a sense of honor in the struggle. If these things are possible to get through with the gumption of a generation, then well it’s my turn. Every person goes through the Four Turnings like seasons through the year. The GI Generation had to fight their wars, and that was the previous crisis (about 80 years ago!).
Before I get to my point, I just want to point out a couple big problems that I’ve had in my head since listening to it. First, it’s very America-centric. Maybe that’s the point, but I also think if you want to make claims about human nature and society wide trends, choosing a country which has only lived for three 80 year periods is not a great place to start. Also, is there a particular perspective by which we’re making these claims? Is this about “the average person”? Is it fair to choose an oppressed class as the arbiter of what is a crisis? If you choose the narrative that historians use for these things, you’re not actually choosing anyone in within the historical context. It’s like how we write about kings and queens of feudal times, but most people didn’t care about any of that stuff.
Five years after the book, 9⁄11 happened. Which by the America-centric view of the book would most certainly count as the crisis catalyst. In their list of potential catalysts for a crisis era, they even mention a terrorist attack on the homeland. Since then, it doesn’t seem like the crisis is going to end anytime soon. We ended our wars with Iraq and Afghanistan. America right now is the closest thing to peacetime as we can expect. But do you feel like we’re in peacetime? Did leaving Afghanistan actually permiate throughout society? Are we just used to wartime that it stopped mattering entirely except for if you have a personal connection to a wounded veteran? Or was the war effort in the later years so pitiful because we were just trying to prop up a government, instead of actually fighting? All this to say, if the crisis was started with 9⁄11 and the consequential (illegal and unjustified) wars, why did ending them not result in …anything? No one celebrated, no one cared, the media and the Republican party just got an excuse to get some easy dunks on Biden, everyone moved on. Trump and the current Republican party sprung up during this 20 year period - a new crisis. And unless 2024 is a blowout victory for Dems (it won’t be), they will persist in our government causing havoc. And then there’s dealing with the effects of climate change.
Someone’s internet diet is much better tell of who they are than where they are from. Internet space replaced physical space in this sense. I used to think that the generational gap exists with people who remember times before the internet existed, and those who see the internet as a natural fact of life. I’m very much in the latter. But, as more younger people have different internet habits than anything I’m familiar with (see: using tiktok as a search engine), I’m realizing that someone’s internet diet can vary so much among all ages that it stops being a useful fact to look at. Yes, content on the internet skews towards younger people, but any more generalizations beyond that and you get into vibe based territory, especially for something like YouTube. Say age was a useful metric for determining someone’s internet diet - couldn’t you just ask them about it? Couldn’t you get their interests and habits from other things about their personality? In what circumstance would you use age to assume stuff about someone and actually act on that assumption? You can’t even mention technical literacy, because there are people of all ages that aren’t comfortable with technology. Maybe they know about their corner of technology - music creation or running a plex server, but not how to forward a port on their router or the minutiae of Windows settings.
Generations can be a useful tool for tracking a cohort with things like homeownership, how often they change jobs, or their average savings accounts. These are economic data points that are real, actual data points. Things we can measure, and generational gaps might exist and we might use the framing to answer questions about the data. But I’m a bit tired of someone asking “Am I a millenial or a Gen-Z?”, or even worse, debating what the “cut-off” is of any given generation. The cut-off doesn’t exist! What do people think? Is there some authoritative body that makes these kinds of things? Who would make them? Why should we listen to them? I think if someone asks this question, it says a lot about how they view information as a whole. I get not having a good grasp of how something like this might work, but asking this question tells me that your critical thinking of new ideas is really, really shortsighted. What I really answer with is that different people come to different conclusions about the exact years, but let me pull up the wiki for the general sense. I wish I could figure out some way to bait this conversation with an unsuspecting victim and socratic method my way into making them realize they don’t just know nothing about how the concept of research on generations, but they have no idea how ideas work. And there’s not much to it! Tracking people by generation is mostly tabloid nonsense to get older people to freak out about younger people doing things differently, or to judge them for their avocado toast.
I think that’s why Millenials aren’t engaging in this as much with the judgemental discourse. They entered the economy just before, during, or soon after the 2008 recession. They get that it’s rough out there, and they remember the “Me Me Me” Generation nonsense. I’m not against using these labels to describe cohorts - I’m saying that people 99% of the time use the labels to draw vibe-based conclusions to the point where the concept shouldn’t be taken seriously anymore. So few non-economic things in our society are delimited by age. And for the things that are, you still can’t construct a grand narrative. If you do, I’m immediately suspicious.
Here’s an example:
IDEA: Boomers are greedy because they use their outsized influence by way of voting, lobbying, and capital allocation to not build new housing. This causes an excess strain on younger people to pay more and more for rent, in a world where wages are not keeping up with inflation.
I’ve heard this take a thousand times, but if you swap “Boomers” with “Homeowners”, it makes more sense. Homeowners want to protect their investment, and as long as housing is treated as an investment, it will be against their interests to build new housing. Woah! We just swapped out a meaningless label for a useful one and didn’t have to adhom at all. It’s never about age! Sure, older people have higher rates of homeownership, but focusing on the older people is a misdirection. It’s never about age.
Also, I’ve always been interested in the non-American aspect of this. Do all western countries use the same age brackets for these things because of WW2? Did they just copy the Gen-X/Gen-Y/Gen-Z/Gen-A convention because of American researchers? Did they copy it all? Do people in other countries use them just through the cultural osmosis of the internet? Do non-English speakers make these categories?
What if instead of generations being when people were born, we talk about it from when the enter adulthood? Someone who is older than me, but lived with their parents longer and was particularly sheltered probably doesn’t have the same cultural understanding of their cohort. Maybe they relate to younger people being their growth in childhood was slowed. Entering the workforce or otherewise adulthood is a much more meaningful year for thinking about this than when someone was born.
People talk about pop culture from when I was a kid, and I have no idea what they’re talking about. You can’t expect someone to be familiar with pop culture for every year after they turn a certain age. And if that’s the case, then saying “Gen-Z nostalgia” is a nonsense phrase. You can have nostalgia for cultural moments that have passed! That’s more than okay! It’s just when people use the generational labels, it is an absolute, otherwise you would use some other label. If you admit “yeah of course not every Gen-Z is going to know this, in fact some Gen-Z’s are going to be born after this cultural thing!” Then you’re just using a funny label because you can’t think of a better one (or you want clicks from people debating whether something is or is not Gen-Z).
I do have a suspicion, that my hate of using generations as a lense to view society is more towards how people use it than the idea itself. Maybe if I read the original two books on this, then I’ll find out that there are more applications than just tracking people of different generations throughout the economy. So, I think I’ll read/listen to Generations (1992), and The Fourth Turning (1996), both my Neil Howe and William Strauss and come back to it. I want to have some evidence that our current situation was destined, and the conclusion is hopeful.

This post started inside of google docs where I was planning a video I wanted to make. Yes, a video! I want to make video content. And to do that, I want to lay some groundwork. I want to do it right. So, I want to make an Introductory Video, much like how I make an introductory blog post.
Before we get to my video content ideas, a quick aside regarding the website.
Natalie and I have really fun ideas for the website. I want to do things like have an email list, have a comments section, make an email server for @reluekiss.com, and other stuff. The problem is Astro (the JS framework that makes this website now) is not Real Programming. I enjoy Real Programming. When I run into a problem and solve it, I can rely on my previous Real Programming skills to help me, and afterwards I add that problem to my stack of Real Programming skills. When I have an issue with Astro, like in the
import.meta.globsituation, there is not a Real Programming skill that will tell me what the right answer is. All of this to say, I’m rewriting the website in Go here soon, and you can bet your bottom dollar I’ll be making posts about the process. Specifically, hosting SSG vs SSR (the normal way) is something I’m super curious about.
Now let’s talk about this video.
The purpose in making videos in the first place is just so I have more reason to look into new things, learn, and because I think it will be fun. The video gets me to explore a topic and it makes me form my thoughts into something coherent, which also helps with learning. Mayhaps I am only learning a topic for a video, and that’s okay, as long as I find it interesting and it’s not a very low hanging fruit.
I would like to talk to the some-number-of-people whose brains click in the same way I do. And because rephrasing isn’t enough, I could contribute more by making the tutorial code Real Programming code. As in, maybe not production ready, but not awful. I don’t like how people in tutorials constantly tell you “this is not how you would really want to do it”. It’s like, why are we talking about this at all then? I am not looking for the bare minimum, worst-but-also-easiest implementation, I’m looking for something that is useful to me. If that’s too much to talk about, then break it up, handwave sections of the example code and tell your audience that it’s in the linked git repo, explain it in another video (or blog post!), but at least put in the effort to make it not awful because “it’s just an example”. I can also accept the “common” way to do something, and then throw in where you would optimize. For example, using gorilla/websocket is a common way to get started, but in this talk, Eran Yanay points out a bunch of different optimizations you can do. I’m not saying every tutorial needs to be this quality. I’m just saying the right answer, or something that helps lead you to the right answer, should be more prevalent than the wrong answer. I want to do a video + blog post about auth because holy moly there actually is zero resources for a production-ready roll-your-own-auth thing (probably because there’s so much money in auth).
This blog is one of my favorite things. Video is not a replacement of this beautiful
website Natalie and I have been making. It’s more like I have an idea of what good content
looks like in my head, and sometimes video makes more sense. For example, when I do posts
like the import.meta.glob, I get frustrated at myself for not being able to express what
I want. Writing is hard! Like, really hard. And I think that a video where I do a code
tour is a much simpler way to teach. Other posts, like my Abstraction
Essay don’t click in my head as a video. I
think I wrote it really well, and it is very much an essay! Video and writing are not
mutually exclusive.
I think it would be cool to make a blog post, and then a video going into things that I couldn’t fit in it. I cut a lot to keep the shape of a post relevant. When I go on a tangent in one of these posts, I feel like I’m losing grip of what I’m trying to express. But I like rambling! There has to be a middle ground. Maybe one of these days I’ll make a blog post where I’m not allowed to delete a sentence if it has already been written. This post has almost none of its original contents from what I had planned.
Feel free to join our IRC channel to talk.
tv.reluekiss.com:6697

Dear Natalie,
Sometimes I’m annoyed at my own music habits. In 2021, I had a bit of a mission to actually get in to music. I’ll make playlists, curate them to ever which mood that I listen to music in, I’ll track those habits and change my playlists accordingly. At the peak, I was managing probably 15 or so playlists. I had around 40, but a lot of them were seeds I was planting that never saw the light of day, or playlists that I had just for statistics. Sorting the songs is something that I prided myself in. When I turn on Spotify, I’m reaching for something. I found all of the things I reached for and made a playlist for it.
But I got a bit carried away. It became a massive spider web to keep up with. Two examples of this would be It’s Called: Freefall and Wishful Thinking. Both of these could belong in CALM, (which really just turned into indie rock), JAM, and at least Wishful Thinking could belong in DREAM. To help with this, I had this thing where the first four songs are immovable. If I had this question, I would look to those four songs. They made up the thumbnail! It’s basically a Constitution.
Curating these playlists followed a simple workflow. First, I would find music by using the radio or through traditional methods. Normally, this was recommendations from friends, tiktok, the Spotify radio, and my personal favorite: shazaming the song that someone is playing in their car while I’m riding with them. It’s great when I’m riding with someone one day, steal a song or two, and then when they’re riding in my car, they hear their song! And I am very forthcoming about how I stole from them. If there’s a universal rule I can say about humans, it’s that they like getting their taste in music validated.
Once a song is selected, next it’s added to Liked Songs. Liked Songs is cheap. Everything goes through Liked Songs. Liked Songs is ephemeral. The goal of Liked Songs is to not have Liked Songs. Liked Songs is the firewall protecting my playlists from having hastily-added songs in them. A playlist should not have a song in them that wasn’t first listened to thoroughly for quality and relevance. The last step of the process is to purge Liked Songs. Either promote promising pieces into perfectly procured playlists, or rescind the refrain’s reservation, recognizing return is rare.
The funny thing is, I did not do the final step in all of 2023. Today I cleared 173 Liked Songs - the earliest dating back to November 2022. This is what I meant by my listening habits sometimes annoy me. I stopped listening to Spotify as much in favor of >1 hour mixes/albums on YouTube, notably post-rock, lofi, synth, and more recently breakcore and jungle. All of these genres have something in common in that it’s hard to distinguish between any particular song unless you have a deep appreciation. I can remember a rapper’s voice, but a guitar solo? It’s hard to pin that on a specific artist. As such, discovery seems more difficult and why bother when the mixes that the YT algorithm gives me is so good? I can tell your nonsense detectors are going off because they’re going off for me too. I’m kind of dependent on the algorithm, not just to new find music but to play music that I like! I do have a YT playlist of certain mixes/albums I like, but I’m not meticulous at all at keeping up with it.
On average, my time listening to music on YouTube is worse than on Spotify because of how I use the platforms. I have missed listening to songs like the one we listened to the other night, Posing For Cars by Japanese Breakfast. Focused listening is what I need more of. Post-rock and lofi are great, but they don’t make me sit back in my chair, interrupting the article I was reading or the code I was writing. Sometimes I don’t want to be interrupted, but what I’ve learned is that there is such thing as too much background music. I should allow myself to be interrupted by fantastic music.
Tonight I finally put in the elbow grease into fixing my Spotify. I purged my Liked Songs, even though I probably hastily added some and unliked others. I also combined several playlists together - such as my COPII and DARK playlists, now it’s just ‘synth’ (new name pending). This is because there is a surprising amount of overlap between a typical song in COPII (named my friend who got me into this music), Moonlight by XXXTENTACION and songs like почему,почему? (why,why?) by niteboi that were in DARK (very creative naming, I’m aware). If you don’t think they are similar, that’s fine, I could probably find a better example.
These combinations were hard to do, and I did them to my favorite playlists. I hope this consolidation makes the choice overload issue go away, as well as allowing my to properly sort songs to where they should go. A symptom of too many playlists is that the same song might go in two or three, possibly four! That’s not great! I’m thinking about making a website where it finds all of the duplicate songs and forces me to pick one playlist to put it in. Might be fun.
It’s funny because it’s almost like I brought technical debt into Spotify. Just like my Factorio world, and most certainly my playlist-powertools project, sometimes a refactor is necessary.
I’m very excited to listen to music with you.
Love you sweetie,
Nathan
This site came with some interesting challenges for me. I wanted to
make a statically generated site, that is accessible to Natalie, that
allows for .html or .md/.mdx and has good primatives for making sure
what we create on here isn’t lost to the sands of time. One of those
things that we did was making a little object at the start of the posts
for who wrote it, the date, the title, we’ll go more into it in a
minute. Tricky thing is, I wanted to customize the AstroJS build step,
something that is tricky using the Astro.glob function they
provide. If you use this, you have to use it within a .astro file, and
if you’re within a .astro file, then you can’t really import a specific
JS function. Luckily, Astro.glob
is just a wrapper around import.meta.glob.
This means we can use our own glob function, and put it in a .ts
file.
This post is going to explain how to get
import.meta.glob to do what you want it to. If you’re
underwhelmed by getCollection and the other default tools
that Astro provides for content management, this is for you. For those
uninitiated, say you have .mdx files inside of blogs/, and
your webpages inside of pages/. You might use either of
these functions to not just parse these files, but Vite will do some
really cool stuff with actually understanding the content of the file.
Check the docs for more, because our usecase is very specific: read
.astro/.mdx files, grab the metadata from them, do some extra
processing, and make a new route for them on the website. All we have to
do for this is create the file inside of blogs/, and make
sure the thing builds. If it builds, it’s good to go!
Here’s what we want to write:
export type BlogDetails = {
title: string;
date: string;
author: string;
overrideHref?: string;
overrideLayout?: boolean;
description?: string;
image?: string | string[];
tags?: string[];
hidden?: boolean;
aria?: { [x: string]: ImageAccessibility };
};And here’s what we want to work with throughout the code:
export type ImageAccessibility = {
alt: string; // a description of the image
role?: astroHTML.JSX.AriaRole; // list of image roles: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles#roles_defined_on_mdn
ariaDescribedby?: string; // if you describe the image in an HTML element, use give it an it like id="carpark-description". that way the screen reader can say "this div describes the image"
loading?: astroHTML.JSX.ImgHTMLAttributes["loading"]; // set to "eager" if image is essential to the post, "lazy" if it is not. default of this is lazy.
};
export type Image = {
url: string;
size: string;
ext: string;
filename: string;
fullname: string;
accessibility: ImageAccessibility;
};
export type Post = BlogDetails & {
id: number;
globbedImgs: Image[];
relativeUrl: string;
absoluteUrl: string;
dateObj: Date;
Component: AstroComponentFactory;
};So two goals: make a route to each of the blog posts, and shape them
into Post objects.
Example of BlogDetails` inside of a .astro file
---
export const details: BlogDetails = {
title: "Images-in-the-terminal",
date: "2024-Jan-01",
author: "natalie",
image: ["LainLaugh.gif", "ncmpcpp.webp"],
aria: {
"LainLaugh.gif": { alt: "an animated girl laughing" },
"ncmpcpp.webp": { alt: "a terminal window with a music playing program open, complete with song picker and audio visualizer", },
},
};
---In .mdx files, we would make it in yml format, like this:
---
title: Looking Forward to the Future
date: 2024-Jan-20
author: nathan
image: excitementometer.jpg
aria:
excitementometer.jpg:
alt: a gauge of excitement, towards high
---Astro does provide a useful type definition for this, in
types.ts, we can do something like this
export type BlogAstro = AstroInstance & {
details: BlogDetails;
};
export type BlogMdx = MDXInstance<BlogDetails>;And this is where the fun begins. First, we make a function that combines these two into something we can work with better.
export function extractMetadata(i: BlogAstro | BlogMdx): {
details: BlogDetails;
component: AstroComponentFactory;
dateObj: Date;
} {
if ("details" in i) {
return {
details: i.details,
component: i.default,
dateObj: parseDateString(i.details.date).dateObj,
};
}
if ("frontmatter" in i) {
return {
details: i.frontmatter,
component: i.Content,
dateObj: parseDateString(i.frontmatter.date).dateObj,
};
}
// this throw won't fire unless you ignore typescript. I like just like errors (golang arc)
throw new Error(`Input: ${i} is not a valid BlogAstro or BlogMdx`);
}Notice the component key. This is something Vite (the
import.meta.glob people) and Astro (who is made using Vite) gives us
when we glob. The i.default, or i.Content
represent the html of the file.
If you’re copying this at home, you can look at the type definitions
of MDXInstance and AstroInstance for yourself. Maybe there is other
information baked into them that you want. For us, we extract those two
things and move on. The parseDateString() is just a
function that either parses the date or throws. We have it in another
function just for the throwing angle (Did I mention I like to find bugs
at compile time instead of runtime?)
I’m pretty sure it’s already html at this point for astro/mdx, but in any case it’s not relevant. What’s important to us is the mdx and astro globs into two different types, so we should combine them into one type. I considered naming this type but it’s just used once. It’s an intermediate step, and a quick one, so let’s move on.
Here’s the star of the show: globBlogs()
export async function globBlogs(
limit: number | undefined,
author: PossibleAuthors | undefined,
hideHidden: boolean | undefined
): Promise<RGlobBlogs[]> {
let combined: Post[] = [];
const interim: ReturnType<typeof extractMetadata>[] = [];
const blogs = import.meta.glob<BlogAstro>("/src/blog/**/*.astro");
//^? Record<string, () => Promise<BlogAstro>>
for (const post in blogs) {
const f = await blogs[post]();
const g = extractMetadata(f);
interim.push(g);
}
const mdxs = import.meta.glob<BlogMdx>("/src/blog/**/*.mdx");
for (const post in mdxs) { // Note: "in" not "of"
const f = await mdxs[post]();
const g = extractMetadata(f);
interim.push(g);
}
interim.sort((a, b) => {
return a.dateObj.getTime() - b.dateObj.getTime();
});The generics for .glob are type assertions, so make sure
your source of truth is… truthy. You could zod your way out of this but
to me, if you have good types and throw errors when you need to, zod is
irrelevant in this situation.
The string to the .glob needs to be a literal string - I
don’t know how they know that it’s a variable but they do. It’s okay
though, because we get individual files by passing the file that we want
as the index, and calling it as a function.
Then we sort! Let’s move on…
let count = 100001;
for (const p of interim) {
const id = count;
count++;
let href;
if (p.details.overrideHref) {
href = p.details.overrideHref;
} else {
href = `p/${id}`;
}overrideHref exists in case if one of us wants to make a specific url but as easy and managed like a blog post. Not sure where that would come in. Probably something SEO heavy - like a tutorial. Not this one though.
let imgs: Image[] = [];
if (typeof p.details.image === "string") {
imgs = await globImages(
[p.details.image],
p.dateObj.getFullYear().toString(),
p.details.aria
);
}
if (Array.isArray(p.details.image)) {
imgs = await globImages(
p.details.image,
p.dateObj.getFullYear().toString(),
p.details.aria
);
}We’ll get to globImages in a minute (there’s more globbing!), but the deal is here I feel like it’s easier to remember the syntax if we just allow a string or an array. It would be annoying if the build fails because you didn’t put redundant brackets around a singular string.
combined.push({
...p.details,
id: id,
Component: p.component,
dateObj: p.dateObj,
relativeUrl: href,
absoluteUrl: `/${p.details.author}/${href}`,
globbedImgs: imgs,
});
}And there we have it! One, proper, Post[]. The rest of the function is sorting + filtering, but let’s show it anyways.
combined = combined.sort((a, b) => b.dateObj.getTime() - a.dateObj.getTime());
combined.map((c) => pushBlogToDb(c));
if (author) {
combined = combined.filter((c) => parseAuthorName(c.author) === author);
}
if (limit) {
combined = combined.slice(0, limit);
}
if (hideHidden) {
combined = combined.filter((c) => c.hidden !== true);
}
return combined.map((c) => {
return {
params: { post: c.relativeUrl },
props: { c },
};
});
}Astro’s getStaticPaths() function expects a params key
and an object where the key is what you put as the file name, so for us
it’s [...post].astro, and the value being the url relative
to that file. As for the props, it means we can do cool stuff with it
instead of everything being a basic string. Let’s look at
globImages()
The Vite function import.meta.glob() is not just for
this, it does a whole lot and I’m really interested in using it to
convert our site from Astro to Go when I feel comfortable doing so (the
html templating just isn’t as good yet, and that’s important to me for
Natalie).
export async function globImages(
imgs: string[],
year: string,
aria: BlogDetails["aria"]
): Promise<Image[]> {
const globber = import.meta.glob("/public/**/*.{jpg,gif,png,jpeg,bmp,webp}", {
as: "url",
});
let images: Image[] = [];There is a second argument to it for options, and you can say
{ as: "url" } so we don’t really care about the content of
the file, but the location of it. This function is only ever accessible
through Vite, so it already knows where your root folder is and what the
url would be to get there.
Vite tangent: I’m not sure what the role of Vite is, and I don’t think I like that there’s a step in the toolchain for Vite to have a job. In Go, I’ve been using the builtin http router and building stuff with templ, which is just a templating language that compiles to Go code. It’s pretty great, and I get how it works. In this line of thought, we’re kind of at the behest of the limited documentation of this function, github issues, and blogs like these for interesting usecases and how to work around these primatives. I don’t like this. It shouldn’t have been this tricky for me to figure all of this out, but there’s too much magic fairy dust sprinkled around all of this. No, I haven’t looked in to how Vite works, or how builders in js work in general because I don’t want to have to care to learn about this. I like building stuff, and it seems like these layers make it more difficult for me to learn and iterate for myself. Because, just like getCollections(), the usecase that the library makers think of is almost never exactly what I want. So, now I have to put in a couple hours of effort to get the last 10% that they didn’t bother to create. I’d rather it just not exist and tell me how to do it, because then that code lives with me. See my abstractions essay.
let images: Image[] = [];
for (const img of imgs) {
const i = `/public/images/covers/${year}/${img}`;
const url = await globber[i]();
const fsPath = `.${i}`;
const size = fs.statSync(fsPath).size;
const ext = path.extname(fsPath);
const file = path.basename(fsPath, path.extname(fsPath));
const urlNoPublic = url.slice("/public".length);One of the magic things that I do like, however, is the assumption
that / is the root of the project, and not the root
directory of the machine. Whenever that “just works”, I’m really happy
because I can have absolute paths and not be scared. But,
understandably, the fs and path packages disagree, so we put a little
dot behind it because the astro build (probably
npm run build for you) command is always ran from the root
directory.
Vite yells at me - it says “Don’t make urls in your application point
to /public! Any static url is already assumed to be /public, don’t
worry, we’ve already figured it out, so go ahead and remove this
irrelevant url”. But whenever I do, it doesn’t work, and I don’t
understand why Vite has a problem with pointing to the /public folder.
In the astro.config.mjs, we already rename the asset folder to
/a/, so it can’t be a security thing.
if (!url || !urlNoPublic) {
throw new Error(`ERROR: ${url} undefined from ${imgs}`);
}
if (!aria || !aria[img]) {
console.log(`\n=====\nNo aria for the image ${img}. Consider adding one.\n=====\n`);
}
// else {
// console.log(`aria for ${img}:\n ${JSON.stringify(aria[img])}`);
// }
const defaultAria = { [img]: { alt: "" } };
const accessibility = { ...defaultAria, ...aria }[img];
images.push({
size: formatBytes(size),
ext: ext,
url: url,
filename: file,
fullname: `${file}${ext}`,
accessibility: accessibility,
});
}
return images;
};We have some classic errors, I was on an accessibility kick a little while ago and that console.log used to throw an error. I’m not sure if I should make it throw. If you want more context with the codebase (such as the formatBytes() function that was 100% written by ChatGPT), you can look at the github repo, and if we’ve already rewritten the site by the time you get there, you can just look at the commit logs for January 2024.
I’m not sure how informative this was on how to properly abuse
import.meta.glob() but I hope it helps. Here’s an example
[...post].astro if you’re really stuck.
---
export const getStaticPaths: GetStaticPaths = async () => {
const g = await globBlogs(undefined, "nathan", false);
// console.log(g)
return g;
};
const props = Astro.props as RGlobBlogs["props"];
---
<NathanLayout details={props.c}>
<props.c.Component />
</NathanLayout>Here, we get that params object, and we filtered for
just nathan, there’s no limit for how many routes we want to make, and
we’re not hidding hidden because even hidden posts should have a url to
them. The <props.c.Component/> ends up getting put in
the <slot/> of the layout from
@layouts/nathan/Root.astro.
I hope this helps someone. Thanks for reading.

Thursday was my first proper maintenance day for this site. Not much for new stuff,
design, or anything that people might notice. Just things that make it easier for me to
iterate on the site. Importantly, now the site’s urls are consistent and stable! I was
kind of scared of Natalie linking to a specific post for the past couple weeks because of
this. Also, I added alt text to images in a pretty cool way. Check out the repo if you
want, but the overview is that there’s an object in consts.ts and it maps a filename to
an object that has the accessibility stuff as the value. So, in glob.ts, when we grab
the images using import.meta.glob, it tacks on this accessibility object to the image.
Also, comments coming soon. RSS a little later. Astro is not very interested in allowing
md/mdx/astro files all go into an RSS feed at once.
Here’s a question: “How do I store username and password information securely in a database and give the client a JWT or session token validating their session?”.
This quesiton seems impossible to answer. Even a high level overview, I can’t find. Every tutorial I see is incomplete, uses deprecated the one deprecated JWT library, completely skips over some vital step, doesn’t set up a web server for the JWT to show it actually being used, I saw on that literally hardcoded the user/pass into the JWT creation part. Why would not mention how to store the someone being authed with a cookie or something? It’s like these people are playing a game of telephone from other tutorials, or they cut out the useful bits because they themselves are not sure if it’s the right thing to do and they don’t want to have a security breach on their conscious.
I think I’ll write some tutorials. I’ve been wanting to do some kind of content in general
(kind of like this blog!). I really want to do a writeup on how I used import.meta.glob
to generate the sites here, because it’s 100% a hack and not the way either Astro or Vite
is documented. I also want to show off my
no-magic-stack at some point. In that
stack, I want to make a few sample web apps just to learn. It’s been so much fun figuring
out different things in Go, and I feel like I’m learning software fundamentals by using
it. After I get auth + todo app working, I’m going to make a twitch chat clone (websockets
seem really cool!), and I have a couple more ideas for after that: something to do with
wasm (I would need ideas on something sufficiently difficult for wasm to shine), and a
service status page. Part of this is to learn, part is to show off a cool stack for others
to replicate, and part is because I really want a twitch chat clone that uses
htmx/go/sqlc/tailwind to exist. I also want to flesh out thing like testing, stress
testing (as in requests/sec), and token-based api auth. Maybe you, Natalie, can make some
tutorial or documentation regarding the Latex stuff you had to go through recently (unless
it actually was documented - I just know you were having issues).
Also, Natalie, you asked what I would want to do while you’re over here in the summer. I know around this time I’m going to meet up with our internet friends because they will be just a couple hours away - so if I can make it so you can come to this that would be cool. I will get one thing out of the way, please don’t expect me to be as good of a tour guide as you were when I visited you. Some things are easier over here, I think, because of the driving angle. But I don’t really have anything in particular I want to achieve, except maybe to…
take you out to dinners, to include nonsense american fast food to the point where you have an opinion on all of the big ones
share a campfire at the lake and make marshmallows (it will be summer time!), camping is optional but I want to go camping
drive looking all cool and whatnot and you’re in the passenger seat and we’re holding hands and I say “the music stopped playing! you’re the DJ Ms. Passenger!”
show you around the capitol and the Smithsonian museums, especially the art one because I’ve never been to an art museum and the science one (the natural history one was very much just ok)
take you out to this one korean barbeque place that’s really good (unless there are peanuts I’m not sure if they use peanuts)
shop for clothes and you can pick out outfits for me and then I say that it’s doing too much and you say no it looks good on you and I cave in
walk late at night like we did before and talk and hold hands, and then come home and look into each other’s eyes for minutes at a time
I hope this answers your question :)

I have this recurring nightmare where I make a website, ship it, forget about it for a decade, and then I need to do a decade’s worth of catching up to ever work on it again. This is my personal problem with abstractions. They are really, really great for most software developers who work in companies shipping products. You can rely on there being a software team for the next decade to maintain, and if necessary rewrite, the website so work can continue. Developer experience is important for this reason. If you ship products quicker, your company will be more successful than the competitor that ships products slower (even if theirs is marginally better/faster/more sustainable). Just for clarity, when I’m thinking about this example, I’m referring to NextJS/React/RSC/that whole paradigm. And this isn’t much of an original thought, it’s an argument that makes sense from Theo. In an age where compute and data storage are so incredibly cheap, why bother? Especially before you have properly figured out your problem space, you need to pick technologies that let you quickly add features and rewrite ones that you inevitably mess up.
But what if we had both? What if the ramp-up time for something like the no-magic-stack that I’m building is just the same as something like Remix, NextJS, or SolidJS? What if there were something using go that had a shockingly similar amount of DX? What if you didn’t need 300 node modules to get a todo app running? And really interesting to me, what if your entire website were one single binary that you can ship anywhere (including static files). I don’t know if that binary would be any better than traditional deployments, but it sounds fun. Speaking of deployments, what if they were as easy to make cheap as NextJS and AstroJS? With those, you just use the primatives that they give you, and because the hosting platforms know how the frameworks are designed, it’s plug and play.
Yes, using htmx and go is a different mental model than React, but for me at least, it just clicks. I’m sure other people have the same thing with Server Components, or React Router, or whatever else. This is what makes sense to me, personally. So, when I say how great and easy it is, I need to keep in mind my bias. I’m still a beginner to programming, and I very much remember not that long ago watching tutorial after tutorial of someone who thinks they know what to say to an inexperienced person, but is blinded by the fact that they are not themselves inexperienced anymore. The most I can do is contribute to the conversation with what makes sense to me, personally.
So, what makes sense to me? Well, I’ve struck my personal balance of comfort with abstractions. Go is the middle ground, I feel like it being a fantastic http server language is obvious.
Sqlc is barely an abstraction - you port in your database schema in SQL, you write your
queries in SQL, and it parses these two to output typesafe Go code. If you look at the
.go files it generates, it’s not using its own library code for these queries. It uses
the builtin database/sql for the connections and requests to the database url. It uses
github.com/lib/pq for the Postgres part of the database and github.com/google/uuid for
UUID types. I’m sure there are others, but this is a sense of how extremely typical and
unobtrusive this generated code is.
Tailwind literally is CSS - even if it uses some JS to generate it, and the standalone binary literally uses nodejs, I don’t care because what comes out is a blank, normal CSS file. It’s difficult to call tailwind an abstraction, it’s more like they just did the work of making useful classes for you. All the program does is add in those classes selectively based on what you’re using so that way you don’t ship a 190kB file to every single user.
The most library-code of the stack is between a-h/templ and htmx. templ is probably the
more difficult one to justify. You can think of templ as React functional components, but
aren’t very capable of running Go code within them. It’s really just html templating but
the primitives of actually using it are so much better than anything else. It’s a function
that returns html, accepts and gives LSP support for structs/maps/slices/strings/whatever.
You can’t put that complicated of logic inside of the templ components, but this is fine.
Some might go as far as to say that logic doesn’t belong at all in components that return
html.
And that brings us to htmx. This library I know the least about, is the most “library” kind of piece, and is probably the most powerful out of all of these. Sqlc, tailwind, and templ, all aren’t that different from what they’re supporting (you write sql for sqlc, you still need to know css for tailwind, and templ is just html+go templating), all require very little new knowledge to learn the library, and provide type safety. Htmx doesn’t do any of these. Although there is an htmx-go library that provides server-side typesafety, it would only be useful if it also provided typesafety and LSP support from the client (.templ files) and the server. I believe this is impossible even in theory unless one side (client/server) changes their syntax to allow for static analysis.
Sure, htmx is easy to learn from 20% -> 80%. But first understanding how a server responds with html, what that looks like, the swapping strategies, and what the hell is ajax and why does the documentation mention it all the time?. It sounds so painfully obvious now, but there was a few weeks were I would watch “let’s build a crud app” tutorials and be more confused than before. It’s not really until I built the todo app myself did I really understand what htmx did for me and how to use it. Again, I know it very little because there actually kind of is a lot to learn with it to get really good. But, htmx seems really cool because it allows me to write so much less code for client-side interactivity. It allows me to not care about the client much at all because the server renders the html and htmx sends that html to the dom. Also importantly, it’s one javascript file, of which I can host myself and keep forever. There will be this version of htmx available for free on the internet forever. I could see myself giving up templ because it’s an abstraction that is relatively unstable and I’m sure I could figure out the builtin templating if I cared enough to - but htmx solves so many problems and I assume it’s extremely stable. By allowing me to send html to the client and have the client know what to do with it, htmx is what allows me to build this at all. Because this is the same problem that React tries to solve - and it does it well.
But, I want to care about performance, and I want to not have my recurring nightmare. This stack seems to be comprised of parts that will exist in 10 years. Go will exist in 10 years. SQL will exist in 10 years, and even though the sqlc parser might not update with my database of choice (for now, postgres), by using Sqlc I am getting the SQL muscles I need to learn. CSS will exist in 10 years, and I’m sure the tailwind classes will still work, and I will bet that Tailwind will be updated with whatever new CSS comes out because tailwind is how most people write CSS and I don’t see that changing unless the browsers implement CSS differently. Will templ exist in 10 years? Maybe not, and that will be a shame. It’s this fact that makes me consider using go’s buitin template but the primitives are so good. Also, what I’m writing can so easily be transferred in the future - it’s just html with some for loops! Will htmx exist in 10 years? Almost certainly, and if not, I’m sure this current version of htmx will be just fine for most stuff. If it’s good enough now, it’ll be good enough in the future because AFAIK, there aren’t really any security considerations with htmx.
In the most abstract way possible, this stack is what I think the web “should be”. Say you have a SPA, using a javascript library on the client to do client-side navigation. That is no where near what the web was intended for. Putting aside the performance, SEO, and UX issues with SPAs, they are in my mind, illegal. You shouldn’t make them because you are ignoring, putting aside, and almost mocking what thousands of engineers have spent three decades curating: the web. To use javascript to treat the browser like a native application is, again, in my mind illegal. (This concept is kind of difficult to come across, so I’ve made these statements more pointy for conveyance reasons. I don’t actually think that people should be in jail for making SPAs. I do think it was a mistake.) In this stack, we go back to what the web is supposed to be not because they are arbitrary standards taken on from down high, but because these standards will continue to exist in the next 10 years.
I’m building this stack because I think it will help me rest easy.

I’ve been programming as a hobby for about two years now. I started with Python, moved on to Typescript through React, and since then I haven’t quite moved on as much I would have liked to. The unfortunate reality is that the tooling around web-dev server-side javascript is really, really nice. I’m currently fleshing out some examples in my upcoming project no-magic-stack - a stack using Go’s builtin http router, htmx (super cool, but also popular right now which makes it less cool), tailwind, sqlc (really cool), and templ (really really cool). I have been implementing things that you get for free in javascript land for weeks now and it’s not easy. I think it’s worth it, and once I get up and running on the stack I have some ideas on cool stuff to do, but the tooling just isn’t there. This is important to me because as much as I hate the frontend - I really like making websites. It’s the thing that you are able to show other people with a link, something everyone can understand, you think that what you’re making (once finished) will be, if not a big hit, something that you can hang your hat on. Of course there is pride to be had in every corner of software engineering, and of course it’s vein to care as much as I do about showing off something to my friends. But dang it, I like showing my friends cool projects… even if I haven’t finished one yet.
Sometimes, through the couple years I’ve been doing this, I will have spent 10 hours in a
day on a project. Today was one of those days and I’m really happy with it. Not all of
those 10 were equally productive - much of it was fighting CSS to get the text to wrap
around the images like Natalie wanted it to (it was float-left the whole time!). But I
made a few really solid improvements to the site and to Natalie’s ease of use in making
posts as well as customizing it in the future. I think the types I’ve set up for
everything is stable, and the code blocks are stable, and the images are stable, I’m
really happy with how this has turned out so far.
One of the reasons I haven’t finished a project is because I don’t know how to get off the ground with frontend design. Everything I make is really cool, but when I go to show it to someone, they recoil with just how god awful the css is. So, I try and try to make it look good, not just for them but also for me so I can call it “complete”. Give it the gold star it deserves and move on. Granted, this has only been two projects, but one of them I’ve rewritten at least three times so that’s got to count for something. Natalie brought this superpower to this project where she has a proper vision of what the site should look and feel like, and she did the first 80% of it. I did some more tricky css bits, but it was her vision the whole way through. I don’t know how to make a design that has personality - I copy what I can from flowbite and hope that it doesn’t look like something out of the chatbot.
One really cool idea she had was if there is an image to a post, it is not in the body of the post but rather in the attachments at the beginning of the post. This is extremely similar to image boards but ours is cooler because we can link multiple images and say within the body “Image 2 is a picture of a dolphin”. I like this because it keeps the body unobstructed. To this end, I have attached a screenshot that is the website in its current form. (Edit: I added it and I hated seeing it on the site. Some kind of recursion creepiness combined with it being a lil pretentious. If you want to see it, look a the git history here )
Up next to the site is hooking up a proper database so we don’t have to do this manual file nonsense. Doing it this way seems very prone to error because how will we know if a link is 404’d forever because of some silly move or overwrite or something? I expect this site to exist ten years from now, so I want to act that way. I also want to act in such a way that I don’t have to maintain it for ten years but for some reason that seems unlikely (I have not ever finished a project, remember?).
Also I need to figure out how time works. We make these posts, and the time part is
optional. So, if we have a time, display that. If there’s no time, js will default it to
12:00AM which is logical. So, if you don’t see a time in the future that’s why. But then
we have to figure out how time zones work. When you run toLocalString(), it says it does
it in local time - but what the heck does that mean? We’re on serverless, and we
statically generate the html. I know there’s a way to include timezone as well in the
creation of the date object, so again I might do it I might not. For future reference, for
the time being, I’m in EST.
And, we made all of these changes without sacrificing client-side js. There still isn’t any anywhere (check your network tab !) and if there is, we’ll make a little thingy somewhere about it. I see this as a challenging limitation, as well as a good principle to follow. I think having no client js is worth some amount of sacrifice, just probably not to the degree as Natalie.
Oh, also, I had this idea for Natalie. So the vlogbrothers (hank and john green) do videos to each other on their youtube directed at each other. It started as an exercise back in 2007 or whatever because they felt like it would be a cool project to make them closer as brothers, and I thought that it might be cute for us to do that here. Not every post, but maybe once a week, either of us writes a letter to the other about whatever. And maybe a rule is that we can’t talk about the post except for within our reply (just an idea, that would be impossible to follow through with).
I’ve wanted to make a personal website for awhile, but didn’t have any good reason to. Sure I could post myself, but really the only thing I could think of would be a portfolio site. And given that I’m not particularly interested in getting a web-dev job anytime soon, and also that any web-dev jobs I could ever dream of getting probably wouldn’t be impressed by whatever I’m able to copypaste from Flowbite - that wasn’t interesting. But we had an idea, I said to Natalie that I should make a blog site for us and here it is.
I’m Nathan - and on this side of the site I’m really excited to post about things I’m learning about. Right now that’s web-dev, golang, and (soon) networking. I’m probably not going to post too personal of things at least until I’m comfortable with the potential of my internet and irl identiies colliding - but Natalie is perfectly free to post whatever she wants.
The site is built with AstroJS - something that’s essentially a build tool for html templating. We’re not using any ui libraries (react, vue, svelte, etc), just plain html. Html templating is really all we need, and if I want to do some client-side interactivity then I’ll throw on HTMX and play around with that. Natalie really cares about not having client-side JS, so I’m going to put a flag somewhere whenever I do decide to make client-side JS.
I did want to use something like Go to do the whole SSG situation, but I didn’t want to have Natalie learn any go or templ (a really cool html templating library for go), and even so I wouldn’t know how to do all of the cool SSG stuff that AstroJS comes out of the box with. I have heard of Hugo, and even though that has more stars on Github, I just think that adopting AstroJS and leaving AstroJS will be easier than Hugo. Also, I already know about Astro, the DX is absolutely insane how good it is. And that DX really matters to me because Natalie’s experience in making her posts and her css and whatever it is that she wants is important! I want her to have fun, and the higher the learning curve the less likely she is to have fun.
This said, I very much plan on moving off of Astro to something that is decades-long stable as soon as possible. I really want to have an in-browser post creator that uploads to a database that we host, instead of making .astro or .mdx files manually. Doing it this way makes sense for what I wanted (get a site up to production as quickly as possible so Natalie stays excited about the project) - but it’s not what I want for the long term. I’d like to have a router written in some language (it really doesn’t matter, I’m just on a Go hype train right now), dependent only the standard library (something with Go is known for!). This does, however, come at the cost of instead of caring about abstractions and third-party libraries - I have to care about and maintain my own code. There’s definitely a situation in which this does not payoff. But I think if I write it well enough and document it well enough then I could make a solution that lasts decades. Or maybe that’s a pipedream.
But yeah, next step - separate the data (these posts) and the view because the fact that they’re together is going to make more work during the eventual migration. But first first, Natalie needs to write some css…