<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>k&#39;s web log</title>
	<subtitle>Just Another Tech Blog</subtitle>
	
	<link href="https://kristinvalentine.com/feed/feed.xml" rel="self"/>
	<link href="https://kristinvalentine.com/"/>
	<updated>2025-06-27T00:00:00Z</updated>
	<id>https://kristinvalentine.com/</id>
	<author>
		<name>Kristin Valentine</name>
		<email>noop@example.com</email>
	</author>
	
	<entry>
		<title>Big AI Feelings</title>
    
		  <link href="https://kristinvalentine.com/posts/2025-06-27-oh-no-ai/"/>
    
		<updated>2025-06-27T00:00:00Z</updated>
		<id>https://kristinvalentine.com/posts/2025-06-27-oh-no-ai/</id>
		<content type="html">&lt;p&gt;I&#39;ve been drafting a blog post in my head about AI for months, but my feelings are strong and my thoughts scattered. At the moment, when I hear the word &amp;quot;AI&amp;quot; my stomach clenches. So yeah, this is a pretty pessimistic/grumpy take, but I need to get it all off my chest before I can come up with anything with nuance.&lt;/p&gt;
&lt;p&gt;My first real awareness of what we call AI today (LLMs) centered around art theft. Stealing authors&#39; words and illustrators&#39; styles with zero remorse. It was just a bunch of tech bros thoughtlessly taking from others so they can feel &amp;quot;Creative&amp;quot; and show off their fun new toy. Claiming that their technology is the future, don&#39;t try to resist, it&#39;s their right to steal from others. Sound familiar? Suffice it to say, this relationship started very poorly.&lt;/p&gt;
&lt;p&gt;It has evolved into environmental degradation, the quickening enshittification of the internet, and better, more convincing scams then ever. CEO&#39;s mandating the use of AI as they lay workers off and burden workers with larger and larger workloads, as investors froth at the mouth at the idea of not having to employ anyone.&lt;/p&gt;
&lt;p&gt;So as you might imagine, no—no I have not tried using AI agentic coding assistants. Just the idea of being forced to use these tools, whether by direct mandate or increasing work loads that are impossible to carry, makes me want to leave tech immediately. But at the moment, it&#39;s hard to imagine that this won&#39;t eventually be the case for everyone.&lt;/p&gt;
&lt;p&gt;When you work in tech, you sign up for constant change—one of the core values of any software engineer/developer is continual learning. The tools we use are always evolving, so you must keep up or get out (or move up to management). Trust me, I know this—I was a Flash developer, so my main tool of choice fully disappeared from existence once already.&lt;/p&gt;
&lt;p&gt;But what do I do when all things point to something that I find morally repugnant? What we call &amp;quot;AI&amp;quot; today is a large set of different tools, and it&#39;s a little naive to just bucket them all together and call them &amp;quot;bad&amp;quot;. But lets be real—it&#39;s not really the tool itself, it&#39;s everything around it, and there is just no way to separate the tool from its context.&lt;/p&gt;
&lt;p&gt;We&#39;re in the middle of the storm—the tools are new, investment is still pouring into anything with a sparkle emoji, and the destruction is only really starting to be realized. It&#39;s hard to say what anything will be like when the dust settles.&lt;/p&gt;
&lt;p&gt;Don&#39;t get me wrong—there are plenty of ways that AI is actually helping society, like improving medical diagnosis. But from my small vantage point, it&#39;s main purpose seems to be ensuring no one has to have ideas or creative thought anymore, we just edit whatever garbage the machine gives us—which of course becomes a cycle of slop, since &amp;quot;generative&amp;quot; AI is just serving us back whatever we gave it in the first place.&lt;/p&gt;
&lt;p&gt;My hope is that we quickly realize how Not That Great AI actually is for most things and we can all just chill. Including myself—because if I&#39;m being honest, for career reasons, I will almost certainly have to use the tools to stay relevant in my industry. The only way I will be able to use these tools is if I am interested and curious about them—and at the moment, the only thing I feel is intense hatred.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Project Stardust: more docs!</title>
    
		  <link href="https://kristinvalentine.com/posts/2023-09-27-stardust/"/>
    
		<updated>2023-09-27T00:00:00Z</updated>
		<id>https://kristinvalentine.com/posts/2023-09-27-stardust/</id>
		<content type="html">&lt;p&gt;In case you haven’t heard, I was laid off from Vox Media in July, like so many other tech workers this year. So a fellow ex-Vox Media teammate and I decided to collaborate on a project together to learn and refresh our memory on widely used languages and frameworks, and to keep programming loaded up in our minds (I spent all of August off the computer, and most certainly would have failed any technical interview after that—&lt;em&gt;programming, yes I think I knew that once…&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;So naturally, we decided to build, drum roll please, a document editor! Yes, yes that is exactly what we worked on at Vox Media, so we have domain expertise and a serious lack of imagination. Unlike the product we worked on, however, we are free to use All The New Things, so I spent a day installing fresh versions of React, Typescript, the latest version of Node(!), Vite, Vitest, and the new-to-me Tiptap, an out-of-box solution for building a collaborative editor.&lt;/p&gt;
&lt;p&gt;Tiptap is simply an abstraction layer on top of ProseMirror, a WYSIWYG editor much like Quill, the editor we were using at Vox. It’s almost depressing how easy it is to set Tiptap up, considering the volume of hours poured into Chorus to create the hand-rolled autosave and multi-user editing systems it pioneered. But, it’s also exciting because it allows us to move past the base functionality that everyone now expects (thanks Google Docs!), to get to the more interesting and unique features.&lt;/p&gt;
&lt;p&gt;What are those interesting and unique features, you may be asking yourself? Well, I am also asking myself, because we haven’t gotten there yet! Before I left Vox, I was working on some interesting mapping features with Mapbox and integrating mini Vue apps into the Quill editor so we could make our custom blocks. I’m excited to see what sorts of interesting, interactive blocks I could create, maybe turn the editor into something more than just a text editor. Maybe a user could draw in it somehow? I don’t know! And that is the fun and exciting part.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Book Review: Do Nothing</title>
    
		  <link href="https://kristinvalentine.com/posts/2023-08-24-book-review-do-nothing/"/>
    
		<updated>2023-08-24T00:00:00Z</updated>
		<id>https://kristinvalentine.com/posts/2023-08-24-book-review-do-nothing/</id>
		<content type="html">&lt;p&gt;&lt;a href=&quot;https://bookshop.org/p/books/do-nothing-how-to-break-away-from-overworking-overdoing-and-underliving-celeste-headlee/9577099?ean=9781984824752&quot;&gt;&lt;strong&gt;Do Nothing: How to Break Away from Overworking, Overdoing, and Underliving&lt;/strong&gt;&lt;/a&gt;&lt;br /&gt;
&lt;em&gt;by Celeste Headlee&lt;/em&gt;&lt;br /&gt;
⭐⭐⭐☆☆&lt;/p&gt;
&lt;p&gt;If you like reading books that are endless citations of cherry-picked studies, than this book might be for you. I really dislike this style of writing, so I guess I should get that out of the way first. I also already agree with the general premise of this book, so I didn’t really need to be convinced of its central argument that overworking is bad. Most of the book is a walk through the history of work in America and the west. While I’ve gone over all this history before, it still struck a chord with me, because I was reading this during a round of layoffs at my company (and since I wrote this last spring, I have since been laid off myself).&lt;/p&gt;
&lt;p&gt;This felt like a Normie version of &lt;a href=&quot;https://bookshop.org/p/books/how-to-do-nothing-resisting-the-attention-economy-jenny-odell/8076119?ean=9781612198552&quot;&gt;How To Do Nothing&lt;/a&gt; by Jenny Odell, which is way more radical and has way less emphasis on individual behavior. While Headlee does write quite a bit about the systemic forces at play, in the end the book concludes with a list of suggestions that amount to “track your time” and “talk to people in real life”. Hey, I’m into those suggestions. But honestly it sounded a lot like “hacking” your day youtubes—just individual behavior bandaids with no real depth, unlike How To Do Nothing, which I think offers a huge paradigm shift in thinking about society and our place in it. Perhaps this is the style of writing, it probably helps that How to do Nothing was written by an artist. Do Nothing will definitely appeal more to the masses, although I personally found it lacking in imagination, and I noticed at least one of her citations was factually incorrect, which throws most of this book&#39;s research into question.&lt;/p&gt;
&lt;p&gt;I can draw many, many parallels between this book and Cal Newport’s writing, like &lt;a href=&quot;https://bookshop.org/p/books/deep-work-rules-for-focused-success-in-a-distracted-world-cal-newport/8339760?ean=9781455586691&quot;&gt;Deep Work&lt;/a&gt;, which may seem a little odd given the names. Basically the idea is the same: if you focus on one thing at a time, you require less time to actually get things done. Isn’t it funny that a book entitled “Do Nothing” is actually about doing your work, but faster. To be fair, she also says “stop overworking and just do what’s required” which is very much the whole “Quiet Quitting” thing.&lt;/p&gt;
&lt;p&gt;I suppose it comes down to the fact that I hated the writing style and felt like it was a real slog to get through her arguments, only to wind up with a few sad suggestions that she obviously thought were quite radical (and to people who have never encountered these ideas before, they could be quite helpful!). But I’m guessing most of us are well aware we spend way too much time on our phones. We can just open up Screen Time to see that horrorshow. I also don’t think “Focus better” is enough. I mean, obviously? If you struggle with this, I highly recommend Deep Work, which has actual concrete steps to learn to work with focus (although Newport&#39;s writing style is also not my cup of tea).&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>In Depth: Quickposts</title>
    
		  <link href="https://kristinvalentine.com/posts/2023-05-20-quickposts/"/>
    
		<updated>2023-05-20T00:00:00Z</updated>
		<id>https://kristinvalentine.com/posts/2023-05-20-quickposts/</id>
		<content type="html">&lt;h2 id=&quot;background&quot; tabindex=&quot;-1&quot;&gt;Background &lt;a class=&quot;direct-link&quot; href=&quot;https://kristinvalentine.com/posts/2023-05-20-quickposts/#background&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our team was tasked with scoping a new feature in the story editor as part of the Verge redesign project, called “Quick Posts”. Originally framed as an editorial experiment, myself and another team member advocated to build this new entry type in Vue as a means of moving us forward in our big rewrite project. Product originally wanted us to do a quicker, dirtier version in our old tech, because we weren’t totally sure if this would stick around in the long term. But because quick posts are a simplified version of the our main compose screen, and only a few select folks would be writing these, I thought it was the best way to build on &lt;a href=&quot;https://kristinvalentine.com/posts/2023-05-15-autosave/&quot;&gt;the autosave work I had completed in the fall&lt;/a&gt; with very limited risk.&lt;/p&gt;
&lt;p&gt;Because of the experimental nature of the feature, the dev plan I wrote was ever evolving as the product plan shifted. I created engineering monthly goals for myself and other team members, given the sizable amount of work and changes to the features. This project also gave us the opporunity to integrate our rich text module into Vue for the first time, so I wrote deep dives on  how it worked with our old stack, how it enables multi-user editing, and how it might work with Vue.&lt;/p&gt;
&lt;h2 id=&quot;quill-in-vue&quot; tabindex=&quot;-1&quot;&gt;Quill in Vue &lt;a class=&quot;direct-link&quot; href=&quot;https://kristinvalentine.com/posts/2023-05-20-quickposts/#quill-in-vue&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Like React, Vue creates a virtual dom, which puts it into direct conflict with anything that wants to manipulate the dom—one of the core features of any rich text editor. Particularly one with its own internal state and document model.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://quilljs.com/&quot;&gt;Quill&lt;/a&gt; uses a document model called Parchment which essentially maps HTML in the editor to meaningful properties and content for Quill to consume. Unlike a “wysiwyg” editor, the output of Quill is &lt;em&gt;not&lt;/em&gt; HTML, it’s a document with an array of instructions and attributes that can be used to generate any HTML, JSON or other output on the fly. It’s also what enables composing content together, which enables multi-user editing and version control diffing.&lt;/p&gt;
&lt;p&gt;Reactive properties can also cause problems with a system designed to use native Web APIs, in particular native dom node events. Quill must compose any outside changes together with existing content, so simply binding content to Vuex state is not possible. Neither is using regular input event bindings for changes—Quill relies on a &lt;code&gt;contenteditable&lt;/code&gt; div element, which is honestly an entire long case study (or three) in and of itself.&lt;/p&gt;
&lt;p&gt;Luckily, Quill does have its own limited set of APIs for setting and getting content. I created a &lt;code&gt;Quill&lt;/code&gt; vue component with an empty &lt;code&gt;div&lt;/code&gt; for quill to initialize inside of, which happens on mount. When the Quill instance is marked as &lt;code&gt;ready&lt;/code&gt;, we initialize whatever contents we have in the vuex state, set up the cursors of other active users, set up listeners for Quill change events, and set up vue watchers for incoming changes from the server or formatting events from the toolbar, so we can compose them into the existing content.&lt;/p&gt;
&lt;p&gt;Thankfully, this is fairly straight forward with simple Quill fields that only contain text. When we tackle the regular story compose experience, we will have to deal with object inserts, like placing an Instagram embed in the middle of a story. This is where a lot of wonkyness with contenteditable and the selection api really rear their heads, and will have to be a story for another day.&lt;/p&gt;
&lt;h2 id=&quot;new-features&quot; tabindex=&quot;-1&quot;&gt;New Features &lt;a class=&quot;direct-link&quot; href=&quot;https://kristinvalentine.com/posts/2023-05-20-quickposts/#new-features&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All of our stories use almost the exact same schema, and we use the existence of specific content fields to differentiate them. For instance, map-based stories (like those found on Eater) have map points. Despite the extremely stripped down-ness of quick posts, we did add a new field to the schema—attachments. Each post can have a single attachment: an image, an embed, a related link or external link (galleries coming soon). We often just use basic text fields for things like links, but in this case, I added an internal linking capability so that a user can input a url and we can find the story (even from other networks) and we’ll save just the ID and an optional override name. This way our audience layer can display internal links with whatever data they want to pull—the original story title, images, the dek, and so forth.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dtzs6w3aq/image/upload/v1693329944/quickpost_vid_gammed.jpg&quot; alt=&quot;An example quickpost on the Verge with a video attachment&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;outcomes&quot; tabindex=&quot;-1&quot;&gt;Outcomes &lt;a class=&quot;direct-link&quot; href=&quot;https://kristinvalentine.com/posts/2023-05-20-quickposts/#outcomes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I spent a lot of time thinking about how we can push our platform forward, given our reliance on unmaintained tools and frameworks like Ractive, while building new features, while supporting the old ones, with a limited number of engineers. No easy task. This is the reason I pushed for Vue quick entries; given our resources, we must be very strategic in how we use every new feature, or even updates to old ones, to continue to move in the direction we all want to head. Every time I touch code, I consider if there is any small refactor that might move us forward, no matter how small the change might be; or how large.&lt;/p&gt;
&lt;p&gt;It turned out to be a good thing we went with Vue, as quick posts have transformed into a first class feature, with The Verge heavily pinning their editorial strategy on it. We worked very closely with the Verge to align our tooling with their hopes and dreams, a productive process that led to a win all around.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>In Depth: Autosave</title>
    
		  <link href="https://kristinvalentine.com/posts/2023-05-15-autosave/"/>
    
		<updated>2023-05-15T00:00:00Z</updated>
		<id>https://kristinvalentine.com/posts/2023-05-15-autosave/</id>
		<content type="html">&lt;h2 id=&quot;first-some-background&quot; tabindex=&quot;-1&quot;&gt;First, some background &lt;a class=&quot;direct-link&quot; href=&quot;https://kristinvalentine.com/posts/2023-05-15-autosave/#first-some-background&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I work on the tooling that our editorial staff use to write and and publish their stories; aka I work on a content management system called Chorus. The UI of Chorus was rebuilt about 8 years ago using a Javascript framework called Ractive (no not React, and no you’ve likely never heard of it). It has your typical 2 way data binding with global events, components, routing, and so forth, a very typical early days reactive JS framework.&lt;/p&gt;
&lt;p&gt;The big drawbacks are that no one knows what it is, it’s not being updated, and it also has many of the typical problems of earlier frameworks: it uses webpack, the state system is less than ideal, there are no types, and so on. It was great for what it was, but the time came for The Big Refactor.&lt;/p&gt;
&lt;p&gt;Before I joined the team, a plan was put in place to move to Vue.js and the team refactored the story dashboard. That route has the least complexity of any other, since it’s just a list of stories and search, for the most part.  It should be noted that almost no one on the team worked on the original product, rather they had worked on our video application before taking over the editor.&lt;/p&gt;
&lt;h2 id=&quot;autosave-complexities&quot; tabindex=&quot;-1&quot;&gt;Autosave Complexities &lt;a class=&quot;direct-link&quot; href=&quot;https://kristinvalentine.com/posts/2023-05-15-autosave/#autosave-complexities&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The most complex part of the app—and the least understood—is its very excellent autosave and multi-user editing features. While some parts of ractive and vue are similiar, state and global events are one of the areas where they are very different, and both are critical to these features.&lt;/p&gt;
&lt;p&gt;The autosave pipeline itself essentially runs in a loop, capturing local changes via operational transforms, sends a packet of changes to the remote server, checks for remote changes from other users, diffs everything together, compares packet sequences, and updates local state. It also saves changes to the user’s local storage in the case of a missed packet or loss of connection that can be merged back in once the user regains their connection.&lt;/p&gt;
&lt;p&gt;Not only is this pipeline complex, it is also business critical, given that we are in the business of publishing content.&lt;/p&gt;
&lt;h2 id=&quot;into-vue&quot; tabindex=&quot;-1&quot;&gt;Into Vue &lt;a class=&quot;direct-link&quot; href=&quot;https://kristinvalentine.com/posts/2023-05-15-autosave/#into-vue&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As you can imagine, I spent a lot of time just &lt;em&gt;reading code&lt;/em&gt;, reading old documentation, and drawing diagrams of the system. We wanted to maintain the reliability of the system, as well as the ease-of-use. It was pretty easy to trigger an autosave from anywhere in the system. However, we could not depend on global event triggers, and we wanted the data to flow through the system in the way that Vue would expect.&lt;/p&gt;
&lt;p&gt;I proposed putting the entire pipeline into Vuex (we were on Vue 2 at the time). The logic was split up into various pieces: Actions triggered events like local saves and parsing incoming remote updates, mutations updated state, and a vuex plugin handles the pipeline logic—setting changes into queues, sending the updates to the server, composing changes together, and rescue logic.&lt;/p&gt;
&lt;p&gt;The entire pipeline is kicked off via mutation—when a document is set as “dirty”, that means a change has been queued and the cycle should begin. The plugin itself subscribes to store mutations to begin the pipeline, as well as an initialization process when the doc first loads in state.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;mutation&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mutation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SET_DIRTY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token function&quot;&gt;sendIfReadyThrottled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SET_DOC&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token function&quot;&gt;initDoc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mutation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I will admit, I don’t &lt;em&gt;love&lt;/em&gt; this. It feels very side-effect-y, and I tend towards a more functional approach. I don’t want magic, I want clarity. But at least in Vue 2 land, this felt like the cleanest way forward.&lt;/p&gt;
&lt;p&gt;The actual state itself contains all of the document content, so when local or remote updates happen, the UI everywhere is updated immediately using vuex state and getters. For throttled and batched updates, the user can see the saving and saved states.&lt;/p&gt;
&lt;p&gt;Any component can dispatch a save —&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;localUpdate&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; docId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; delta &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which makes it very easy to spin up new autosaving components. The &lt;code&gt;delta&lt;/code&gt; is based on &lt;code&gt;json0&lt;/code&gt; transforms, which includes a &lt;code&gt;path&lt;/code&gt; (where in the document to save the data), and an array of operations to make. These changes can look like “at character number 15, insert &lt;code&gt;hello&lt;/code&gt;&amp;quot;, or “delete list item number 4”.&lt;/p&gt;
&lt;h2 id=&quot;results&quot; tabindex=&quot;-1&quot;&gt;Results &lt;a class=&quot;direct-link&quot; href=&quot;https://kristinvalentine.com/posts/2023-05-15-autosave/#results&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This work has been in production for a year and a half without issue, and it unlocked the rest of the Big Rewrite project. The initial implementation of this went live with our updated layout tooling, so it mostly consisted of clicking a few buttons to trigger the save. But since then, we have deployed the compose screen for &lt;a href=&quot;https://kristinvalentine.com/posts/2023-05-20-quickposts/&quot;&gt;Quick Posts&lt;/a&gt; for &lt;a href=&quot;https://theverge.com/&quot;&gt;the Verge&lt;/a&gt; and are well underway for other composition and publishing screens.&lt;/p&gt;
&lt;p&gt;While we are now on Vue 3, we have not made any major moves as far as using the composition API or using Pinia. At the moment, I don’t see any major advantage to moving in that direction besides Typescript, although honestly VS code does a good job of giving hints even without TS.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Scheduling posts, sounds easy enough</title>
    
		  <link href="https://kristinvalentine.com/posts/2023-03-03-what-is-time-really/"/>
    
		<updated>2023-03-03T00:00:00Z</updated>
		<id>https://kristinvalentine.com/posts/2023-03-03-what-is-time-really/</id>
		<content type="html">&lt;p&gt;What are the hardest parts of programming again?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Naming things&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;Date and time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ve been writing a date time picker Vue component for editors to schedule stories for publishing and it is such a beast! The picker itself is now done, but I’m hooking up the scheduler and handling the myriad of possible paths these components can take.&lt;/p&gt;
&lt;p&gt;A story can be scheduled, it can be rescheduled, it can be unscheduled, it can be published and therefore unschedul-able. It can also be any one of those things &lt;em&gt;by someone else at the same time&lt;/em&gt; so we have to respond to remote changes through sockets.&lt;/p&gt;
&lt;p&gt;We also have to handle how to figure out if a story was scheduled already or if the user is currently trying to schedule it—nearly everything on the page autosaves, but not the schedule. Except unscheduling, that does autosave. So how do we know if the datetime that we have in state is the one the user put in, or the one that was already saved, or the one that a remote user just now saved?&lt;/p&gt;
&lt;p&gt;We also have to show the current date/time updating live if the user has not scheduled anything, or we have to show the date and time the story was published on if it’s been published or changed. So we need to make sure we don’t think that any of those numbers mean that the user has tried to schedule it, and we also need to make sure that the user doesn’t choose a time in the past (for now, we will allow backdating in the future). With the older implementation of this process, at some point someone was able to publish a story with a future date and no one knows how that could have happened, so make sure to think about that, too.&lt;/p&gt;
&lt;p&gt;If you are from the future and you notice this was the last thing I ever wrote, it’s because I threw my computer out the window in frustration, quit my job and moved to the woods.&lt;/p&gt;
&lt;p&gt;I think I’ve got all the timezone stuff all nailed down, thanks to our timezone library, so a lot of what’s tricky now is managing a global state (previously scheduled data) versus local state (what the user has inputted) and managing communication between two sibling components that need to be re-usable outside of the parent component.&lt;/p&gt;
&lt;p&gt;I’ve decided to go with &lt;a href=&quot;https://vuejs.org/guide/components/provide-inject.html#working-with-reactivity&quot;&gt;reactive providers&lt;/a&gt;, where the whole shebang is wrapped in a component that manages local state and also grabs the vuex store data to verify what’s what. It seems to be far less cumbersome then what I did originally, which was a lot of emitting of events and repetitive computed properties between 3 components (which all involve comparing dates and formatting them for timezones). It feels &lt;em&gt;a little weird&lt;/em&gt; to pass an injected method to the components to update the local state, but I suppose it is basically the same thing as dispatching events in Vuex.&lt;/p&gt;
&lt;p&gt;I did not use Vuex for this because we want this data to use the lifecycle hooks of this page/component—when you navigate back to the dashboard, we don’t want it to stick around. I’d have to wipe all the data out of the store on &lt;code&gt;unmount&lt;/code&gt; which feels dumb and not like what vuex was meant for. I am guessing that Pinia might be a better solve for this (I actually have no idea), but we aren’t using it yet and I don’t think we plan to any time soon.&lt;/p&gt;
&lt;p&gt;The other trick bit of this is testing. Thankfully Jest has a lot of helpful mock timers; I can &lt;a href=&quot;https://jestjs.io/docs/jest-object#jestsetsystemtimenow-number--date&quot;&gt;set a specific system time&lt;/a&gt; to mock the current time and increment the &lt;code&gt;setInterval&lt;/code&gt; calls &lt;a href=&quot;https://jestjs.io/docs/timer-mocks&quot;&gt;manually&lt;/a&gt;. I&#39;m still figuring out how to test the reactive providers, since I don&#39;t have a ton of experience with this pattern. I think a stubbed mock component to act as the providing source is the way to go; isn&#39;t it great how much javascript one must write to test the javascript you already wrote? What&#39;s going to test the test? That is certainly a (long) topic for another post.&lt;/p&gt;
&lt;p&gt;I think another side of this that makes it extra challenging is that the feature is &lt;em&gt;critical&lt;/em&gt;. If stories don&#39;t publish when they are supposed to, that is a really big problem. Even if it works &amp;quot;correctly&amp;quot;, it also has to be clear to the user when the story will actually publish, which is harder then you might think. We show the user times in the timezone that they set up in their profile, not their system time. If they are traveling in a different timezone, suddenly this all feels less obvious. So we have to put timezone labels on &lt;em&gt;everything&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Don&#39;t even talk to me about daylight saving time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Additional reading on programming and dates&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://zachholman.com/talk/utc-is-enough-for-everyone-right&quot;&gt;UTC is enough for everyone ... right?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>Hello, again</title>
    
		  <link href="https://kristinvalentine.com/posts/2022-11-09-hello-again/"/>
    
		<updated>2022-11-08T00:00:00Z</updated>
		<id>https://kristinvalentine.com/posts/2022-11-09-hello-again/</id>
		<content type="html">&lt;p&gt;I&#39;ve declared bankrupcy on my old blog and am starting over. I was working on replatforming my old site from Jekyll to Eleventy, and reading over the old posts, I just felt like it was time to let those old writings go.&lt;/p&gt;
&lt;p&gt;While I&#39;ve been working on this project for an embarassingly long amount of time, I feel like right now is a real moment of change in a lot of people&#39;s online publishing world. Maybe people don&#39;t think of themselves as being writers (or &amp;quot;content creators&amp;quot;), but that&#39;s what posting to twitter or any other social media site is: Writing things and publishing them to the internet-- content you write for free and owned by someone else to make money off of.&lt;/p&gt;
&lt;p&gt;Those of us who never gave up on RSS are wondering: is it time for RSS and personal blogs to take center stage once more? The sudden rise of personal newsletters is certainly pointing in that direction, although I can&#39;t say publishing to the hellscape that is my email inbox is my cup of tea. What is a newsletter but a semi-private blog post with a list of specific audience members?&lt;/p&gt;
&lt;p&gt;The main difference between something like Twitter and RSS is that RSS is pull only. There are no replies, just backlinking. I&#39;m not sure that that is necessarily a &lt;em&gt;bad&lt;/em&gt; thing, but it does feel less like a conversation and more like a magazine.&lt;/p&gt;
&lt;p&gt;But with personal blogs and RSS subscriptions, not only do you own your own content, but you also curate your own reading experience, without the &amp;quot;help&amp;quot; from algorythms. Some people complain that their RSS feeds &amp;quot;fill&amp;quot; up and they feel bad about it, but I would argue RSS is more like a social media feed and less like an email inbox. You just dip in and catch up as much as you want. You can&#39;t inbox zero the internet.&lt;/p&gt;
&lt;p&gt;I sure hope we see some improvements to RSS as a format as well as the products around it, AND new publishing platforms that make it much easier to put many different forms of content out there in ways that anyone can consume. I think there is a lot of room in this space for new approaches, especially around ways of sharing, linking and responding to content in distributed ways that don&#39;t require everyone downloading the same app and signing up for the same service.&lt;/p&gt;
&lt;p&gt;RSS has been the quiet backbone of the internet for a very long time, and if it continues to simmer without the glitz and clamor of social media products, that&#39;s fine by me.&lt;/p&gt;
</content>
	</entry>
</feed>
