<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Spontaneous Productivity ]]></title><description><![CDATA[Spontaneous Productivity explores how to get things done without overplanning, so you can focus on the work that truly matters]]></description><link>https://blog.nestful.app</link><image><url>https://substackcdn.com/image/fetch/$s_!aZOI!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13336315-f7a5-451b-ae0a-2cf7a1f81850_1024x1024.png</url><title>Spontaneous Productivity </title><link>https://blog.nestful.app</link></image><generator>Substack</generator><lastBuildDate>Sat, 11 Apr 2026 20:50:26 GMT</lastBuildDate><atom:link href="https://blog.nestful.app/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Nestful]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[nestful@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[nestful@substack.com]]></itunes:email><itunes:name><![CDATA[Nestful]]></itunes:name></itunes:owner><itunes:author><![CDATA[Nestful]]></itunes:author><googleplay:owner><![CDATA[nestful@substack.com]]></googleplay:owner><googleplay:email><![CDATA[nestful@substack.com]]></googleplay:email><googleplay:author><![CDATA[Nestful]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[How we dropped Vue for Gleam and Lustre ]]></title><description><![CDATA[Nestful is now exclusively powered by Gleam and Lustre. Vue was completely removed and TypeScript is only used for glue code and FFI.]]></description><link>https://blog.nestful.app/p/how-we-dropped-vue-for-gleam-and</link><guid isPermaLink="false">https://blog.nestful.app/p/how-we-dropped-vue-for-gleam-and</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Wed, 28 Jan 2026 19:30:54 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!aZOI!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13336315-f7a5-451b-ae0a-2cf7a1f81850_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It took a while, but as of today, if you visit <a href="https://nestful.app">Nestful</a> you are going to be served by Gleam &#8212; a friendly, type-safe, functional language compiling to Erlang and JavaScript &#8212; and Lustre &#8212; its front end, Elm-like framework.</p><p>This is the conclusion of a transition that started more than a year ago, stemming from my frustration with TypeScript and the state of front-end frameworks. Gleam and Lustre were and still are a breath of fresh air and I am glad to have made the move.</p><p>This post will detail how those early motivations measure up in hindsight, and what it means (and feels) to maintain tens of thousands of lines of Gleam code.</p><h2>You could do it too</h2><p>Nestful is what I call &#8220;almost done&#8221; software. It&#8217;s not quite the completeness that are SQLite or Sublime Text, but is also not as ever-changing as something like Windows, a bastion of agile development these days. This positioning makes Nestful able to survive such a rewrite.</p><p>Nestful is valuable to users as is, and so they are more tolerant of features not being a part of every browser refresh. Some are happy with the low amount of jarring changes. Not every app has this luxury, with some pushed by their market segment to chase competition. Thankfully Nestful is a very opinionated tool in a niche without much innovation, so could easily afford the rewrite.</p><p>However, a rewrite is not the only avenue. Since Gleam is not a pure language, it&#8217;s very easy to interact with existing JavaScript or TypeScript code. New code and much-needed refactors could be done in Gleam with little to no hassle. In fact, this is how Nestful started, with Vue SFCs written in Gleam, and later with Lustre taking over state management while Vue kept on rendering.</p><p>You could do the same in your company, implementing carefully and incrementally, and enjoy the benefits described in the blog post for those new sections of code.</p><h2>Of joy and trouble</h2><p>Clearly, Nestful does change. &#8220;Almost done&#8221; is very different from &#8220;done&#8221;, with improvements added in a calculated manner. As of writing, the next planned big feature is showing items from third party services. This requires a non-trivial amount of architecture to then be able to arbitrarily add tasks from other services and have them bubble up next to one another for the user to compare priorities.  </p><p>To do that, the code base needed to support changes maintainability-wise, but also the enjoyment of writing them. The thing is &#8212; those are intertwined. Yes, there is an aspect of enjoyment that stems solely from the writing, in isolation. But there&#8217;s also the mental load that comes from bad maintainability which kills that joy of coding.</p><p>In my <a href="https://blog.nestful.app/p/why-i-rewrote-nestful-in-gleam">original post</a> about rewriting in Gleam, I mentioned some of the language specifics that were a joy &#8212; simple, exhaustiveness checks, pattern matching, friendly syntax. Those all turned out to be true. A stark contrast to TypeScript&#8217;s complexity, where one has to write two programs; one serving logic and the other types. All I wanted was to make sure there aren&#8217;t any null references.</p><p>Difficulties on the front end most often boil down to managing state. jQuery closures, Angular services, React and Vue hooks and stores and of course, the resurgence of back-end-centric solutions like Phoenix &#8212; those are all attempts trying to make sure incrementing a counter won&#8217;t turn on the user&#8217;s toaster.</p><p>Elm and its architecture almost entirely solved state issues like the aforementioned by getting state soundness in exchange for boilerplate. You write more defining your state model, the actions that can be performed on it, and the side effects they may incur and in return you get significantly fewer bugs and, most importantly, developer clarity of how state flows. That is a fine exchange. Boilerplate is much easier to live with than being ever-vigilant about state racing like it&#8217;s Daytona.</p><p>Along the years I&#8217;ve advised companies on how to replicate this architecture with TypeScript, solving issues like the that &#8220;spooky actions at a distance&#8221;, unpredictable reactivity, and unruly side effects. I&#8217;ve done it with Nestful. However, there was always this feeling of trying to fit a square peg in a round hole. It&#8217;s well worth the benefits, but could I have done better?</p><p>That&#8217;s where <a href="https://hexdocs.pm/lustre/">Lustre</a> &#8212; Gleam&#8217;s implementation of The Elm Architecture &#8212; shines as the true star of the show.</p><p>Explicit state handling and not having to fight off reactivity made Nestful much faster. The architecture also avoids over-design by allowing the developer to easily compose and recompose state and its handlers, Gleam and Lustre do make refactors easy and clean, because in the end, it&#8217;s all just regular functions with strong type guarantees.</p><p>This all concludes to a single word: fun. Programming Gleam is fun, and I do not intend on going back to the old, cold days of TypeScript if I can help it.</p><h2>Lessons Learned</h2><p>The following is a short summary of some lessons learned along the way, in no particular order (except for that first one). I hope that they can be useful to you.</p><h3>The AI Elephant in the Room</h3><p>I know I&#8217;ll get a lot of replies saying how Gleam is too new for AI and therefore productivity is reduced compared to something like React. Well, I&#8217;ve been using Opus 4.5 for some of the rewrite and I can tell you two things:</p><ol><li><p>It is much better at Gleam even compared to Sonnet 4.5 and Codex 5.2</p></li><li><p>LLMs are very knowledgeable idiots</p></li></ol><p>Given their stupidity as their main weakness and their vast knowledge as their main strength, LLMs need very precise guidance and supervision to crank out what I&#8217;d consider acceptable code. In that context, having more knowledge about a specific framework or language results in diminishing returns, especially with tools like Claude Code or OpenCode which give the LLM more information.</p><p>Instead, it&#8217;s much preferable to guide the LLM to avoid its stupidity.  The constraints that are inherent to Gleam and Lustre do that perfectly. Yes, the LLM might try to concatenate a conditional CSS class instead of using <code>attribute.classes</code>, or rename variables weirdly because it has PTSD from JavaScript name collisions. That is much easier to deal with than having it RNG an architecture like we&#8217;re playing The Sims.</p><h3>Forget view hierarchy</h3><p>This is more of an Elm architecture generality but is useful even if you don&#8217;t adopt a framework that follows it. Due to components holding state, React and Vue make it extremely easy to model your state hierarchy identically to your view hierarchy.</p><p>This is often not only counterproductive but downright harmful. State should be modeled completely separately from the view. Drawing state encapsulation lines that match view encapsulation will just mean you&#8217;ll be ever-busy trying to circumvent your own architecture as you discover state cross-requirements.</p><h3>Design late</h3><p>JavaScript and to some extent TypeScript are both landmine languages and therefore incur significant refactor cost, as refactoring doesn&#8217;t only mean avoiding pitfalls in the new code, but also breakage that is caused by removing the old code.</p><p>This is almost entirely absent in Gleam, which has your back. This makes the drawbacks of designing early &#8212; making incorrect assumptions about the future &#8212; more harmful than the logic inconsistencies that still can sneak into Gleam code, especially since it&#8217;s not pure.</p><p>Instead, just build really well for what you have now. The code will tell you when you need to change the design, which will be a mostly painless process. </p><h3>Accept the boilerplate</h3><p>Lustre requires you to commit to more boilerplate. It is a fact of its design. Fighting it by being &#8220;smart&#8221; will come back to bite you down the line.</p><h3>Consider your FFI boundaries carefully</h3><p>FFI is by far the most bug-prone area of the code. This is understandable. Be careful when writing it and its glue code to Gleam.</p><p>I hope that tooling in the future could detect type inconsistencies between TypeScript types and the types declared in Gleam for the external function. </p><h2>Have fun</h2><p>I may be repeating myself &#8212; well, I am repeating myself &#8212; but remember to have fun. Slight adjustments to the things you do can go a long way in making you enjoy them. Even if you don&#8217;t like Gleam or Lustre (how?!) &#8212; incorporate what you do like, even if in a small way. I haven&#8217;t done my research but I&#8217;d bet people who enjoy the work do it more productively.</p><p>What more do you need?</p>]]></content:encoded></item><item><title><![CDATA[When you’re not working, you’re still working]]></title><description><![CDATA[What happens when you leave an idea alone]]></description><link>https://blog.nestful.app/p/when-youre-not-working-youre-still</link><guid isPermaLink="false">https://blog.nestful.app/p/when-youre-not-working-youre-still</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Thu, 12 Jun 2025 12:01:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!En0E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!En0E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!En0E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!En0E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!En0E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!En0E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!En0E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/febafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2404221,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.nestful.app/i/165767993?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!En0E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!En0E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!En0E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!En0E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebafa28-9a17-4616-8a22-414ce43d2766_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;ve found the most successful way to write these blog posts isn&#8217;t to sit down and hammer out words on a deadline. It&#8217;s to start an idea rolling on Monday, let it drift through the week like vapor, and then, by Sunday, it&#8217;s ready to land. This one began in a half-formed note just after I hit publish on the last piece. I returned to it on Thursday with a few new thoughts and shaped it into something fuller by the weekend. I&#8217;ve started applying this rhythm to other work too, not just writing, and it&#8217;s reminded me that so much of what we call productivity happens off the page, outside the task, in moments that don&#8217;t look like work at all.</p><p>I wrote in an earlier post about <a href="https://blog.nestful.app/p/due-dates-are-killing-your-productivity">how due dates can kill momentum</a> &#8212; that you don&#8217;t have to finish a task in one moment, you just have to engage with it a little. Sometimes that little snowballs and becomes a flow session. Sometimes it doesn&#8217;t, and the task slips back into the background. Either way, it&#8217;s still alive. That first touch-in is enough to start something stirring. I don&#8217;t think we give enough credit to that kind of invisible work&#8212;the work that simmers while you&#8217;re walking in the park, or lying in bed, or staring out the window without realizing you&#8217;ve slipped into the middle of it again.</p><p>The brain has its own way of revisiting things. On my daily walks, I often stumble across answers to problems I wasn&#8217;t consciously thinking about. Noticing how an idea I&#8217;d started earlier in the week has evolved slightly. Two previously unconnected thoughts now sit side by side, quietly linked. Same thing in the shower. In fact, long showers away from my phone feel like a shortcut back to my mind. I know none of this is groundbreaking &#8212; there&#8217;s a name for it, after all. Psychologist Graham Wallas called it incubation. He laid out a four-stage model of creativity almost a hundred years ago: preparation, incubation, illumination, verification. It still holds. I find comfort in the idea that letting go for a while is not just part of the process &#8212; it <em>is</em> the process.</p><p>But I do wonder how often I derail that process myself. I know when I&#8217;ve scrolled too much, checked for updates on too many apps, yanked my brain out of that gentle hum and into distraction. I&#8217;ve started thinking of my phone as the enemy of incubation. I wonder how many ideas I&#8217;ve buried with overstimulation, how many breakthroughs I&#8217;ve drowned out by refreshing tabs that didn&#8217;t need refreshing. There&#8217;s something fragile about that drifting state. It doesn&#8217;t shout. It slips in when your mind is a little bored, a little open, not being asked to perform.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Want more musings on Spontaneous Productivity? Drop your email to stay in the loop.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>The idea of letting things rest isn&#8217;t modern, of course. I first came across the Daoist concept of <em>wu wei</em> years ago and it stuck with me. Non-doing, or more precisely, effortless action. It felt at once obvious and profound. I ended up reading the <em>Dao De Jing</em>, and then reading it again, and again. One line in particular keeps circling back to me: &#8220;Muddied water clears when left alone.&#8221; There&#8217;s something deeply relieving about that. That we don&#8217;t have to fix or force everything. That stillness is not failure, but a strategy. There&#8217;s a kind of bravery in leaving something alone long enough to let it become clear.</p><p>It&#8217;s the same with therapy. I often find myself grumbling about the price of a session: an hour that feels short, especially when I compare it to my own hourly rate. But the more I think about it, the more I realize the session is just a spark. The real shifts happen in the days between. Something unspools in the background. What felt tangled or stuck starts to loosen, not during the appointment, but in the quiet that follows. It&#8217;s the same thing, again. The work we think we&#8217;re doing is only part of it. The real change often happens later, silently.</p><p>That said, not everything needs days to marinate. Some tasks genuinely benefit from being done in one go &#8212; emails, admin, booking the damn dentist. Even creatively, there are days when everything&#8217;s ready at once and the words come tumbling out in a single sitting. But when that happens, it&#8217;s usually because the thinking&#8217;s already been done. I just didn&#8217;t notice it. The bubbling happened in the background. The writing part was just the final stage, the part that looks like work.</p><p>The moments I find hardest are when I&#8217;m expected to come up with something on the spot. A slogan, a concept, a sentence that sums up a campaign. It never works. I always freeze. My brain isn&#8217;t built for spontaneity in that way. It&#8217;s why I get so frustrated when someone says, &#8220;Just write it now!&#8221; as though writing is transcription and not excavation. I&#8217;m not an improv comic. The good stuff comes when I&#8217;ve had time to not think about it.</p><p>What this means in practice is that at any given time, I&#8217;m spinning several plates in my mind. I picture them quite literally, each one something I&#8217;ve nudged into motion. Some are spinning fast and don&#8217;t need me yet. Others wobble and ask for attention. The trick isn&#8217;t to keep all of them moving constantly &#8212; it&#8217;s to know when to step in and when to let them be. That&#8217;s what I&#8217;ve found Nestful quietly supports. The nesting function lets me break down big things into manageable parts. It gives me a bird&#8217;s-eye view, so I can dip in, check on something, and leave again. It mirrors the rhythm of thought itself &#8212; touch in, retreat, return.</p><p>There&#8217;s a strange joy in coming back to something that&#8217;s been quietly cooking in your mind. When you open the doc and find the shape is already there, you don&#8217;t need to push, you just need to tinker. The effort has already happened. It&#8217;s like skimming the fat off the top of a broth that&#8217;s been simmering all day. All that&#8217;s left is refinement.</p><p>And all of this &#8212; this drifting, circling, returning &#8212; it doesn&#8217;t happen well if you&#8217;re exhausted. It hinges on good sleep. Not just quantity, but quality. Regular, unrushed, properly dark sleep. I&#8217;ve noticed a direct correlation between my creativity and the nights I sleep well. Not because I wake up with epiphanies, but because the baseline of my brain is calmer. It gives me more chances to slip into the space where good work can happen.</p><p>So when I say I&#8217;m working on something, I often mean I&#8217;ve nudged it forward, let it go, and trusted it&#8217;ll come back when it&#8217;s ready. Sometimes it surfaces while I&#8217;m walking. Sometimes while I&#8217;m showering. Sometimes not at all, but even that&#8217;s okay. There&#8217;s no real rush. The plate keeps spinning. The water clears on its own.</p><p>Try <a href="https://nestful.app/?utm_source=substack&amp;utm_medium=blog&amp;utm_campaign=juggle">Nestful</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/subscribe?"><span>Subscribe now</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/p/when-youre-not-working-youre-still?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/p/when-youre-not-working-youre-still?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[What productivity systems often get wrong about time ]]></title><description><![CDATA[The quiet mismatch between how we experience time and how we&#8217;re told to manage it]]></description><link>https://blog.nestful.app/p/what-productivity-systems-often-get</link><guid isPermaLink="false">https://blog.nestful.app/p/what-productivity-systems-often-get</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Mon, 02 Jun 2025 18:40:37 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!oDcz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oDcz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oDcz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!oDcz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!oDcz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!oDcz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oDcz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2242975,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.nestful.app/i/165033935?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oDcz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!oDcz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!oDcz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!oDcz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c04c50a-b450-4ecd-ac9f-7435387ae059_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There&#8217;s a familiar urge to wait for the top of the hour. Or the half hour. To not begin at 2:17PM, but to wait for a neater time: an official start. It&#8217;s easy to tell ourselves it&#8217;s just about procrastination, but it&#8217;s deeper than that. It&#8217;s about wanting time to feel clean, structured, legitimate. We&#8217;ve been taught to treat productivity like meal prep: clear the counters, lay out your ingredients, block off a whole afternoon. But most of life isn&#8217;t Sunday. It&#8217;s midweek noise, a surprise call, a delayed start. A half-hour that turns into thirteen minutes and a question: is it worth starting now?</p><p>What if productivity didn&#8217;t rely on perfect containers? What if it moved with time&#8217;s actual texture, fragmented, shifting, often unresolved? What if the aim wasn&#8217;t to stay perfectly on track, but simply to keep hold of the thread &#8212; to return to what matters, without the sense that you&#8217;ve failed just because you didn&#8217;t follow a line?</p><h2>How do you see time?</h2><p>I&#8217;ve been thinking a lot about how I perceive the shape of time and how that might be wildly different from those around me. Of course, how we <em>see</em> time affects how we move through it. For me, sometimes time looks like an agenda &#8212; neat little blocks, like a projection of Google Calendar in your mind &#8212; it can feel rigid, maybe even oppressive. But it can also feel satisfying. Tasks slot in. Hours get filled. Things get done.</p><p>Other times, time isn&#8217;t structured at all. It&#8217;s just a digital clock with numbers slipping forward whether I move or not. It doesn&#8217;t wait. It doesn&#8217;t care. There are no blocks, no cadence &#8212; just the slow dissolve of minutes into nothing. You&#8217;ll notice my perceptions of time are based on technology. This wasn&#8217;t intentional, but it&#8217;s deep rooted from the way we learned everything we know about time &#8212; through ticking timers, progress bars, and the white space of empty calendar invites. These tools have shaped how we all relate to time: not just as a backdrop, but as something to manage, fill, and spend.</p><p>I once did a 10-day silent meditation retreat: no speaking, no phones, no clocks. A bell would ring at 4:30AM to wake everyone for the day&#8217;s first meditation, and it would ring again throughout the day to signal transitions. We moved with the bells, and the sun became the best indicator of time. And yet, even in that setting, I still found myself grasping at it. I remember sitting in meditation and wondering how long I&#8217;d been sitting, how long was left, when the next bell would come. Even in the stillness, I couldn&#8217;t let go of time, not really.</p><p>The retreat felt like a slice of unreality. But the moment I left, I slipped right back into timekeeping. Almost instinctively, I returned to counting hours, checking clocks, measuring my day in terms of output and intention. I often catch myself thinking: I have a box of time here &#8212; one hour. Should I apply it to a task? Or should I lie in bed, listening to the radio? Both, in their own way, feel productive. That framing &#8212; the box, the trade-off &#8212; is deeply ingrained.</p><p>There are moments when time disappears altogether. There&#8217;s no clock, no list, no &#8220;next.&#8221; Just now. I&#8217;m here, and that&#8217;s all there is. With practice, these moments come more often. When they do, I notice. I&#8217;m like a kid balancing on a bike for the first time, my brain goes &#8220;Look! I&#8217;m doing it!&#8221; Urgency evaporates, objects become sharpened and everything gets a lot more vivid. It&#8217;s not a psychedelic trip, but it feels adjacent. It&#8217;s just presence. And then, of course, I fall back into the grid. The spreadsheet of hours. The pressure of minutes. Because, like everyone else, I&#8217;ve bought into the collective contract of time. We agree to measure it so we can move through the world together. It&#8217;s how we meet deadlines. Catch trains. Sync calendars. Time, distorted as it may be, still functions.</p><p>It also forms the backbone of how we approach productivity. But if time feels different for each of us, doesn&#8217;t that complicate things? Maybe the problem isn&#8217;t how we manage time, but how we experience it. Or more importantly &#8212; how our tools assume we experience it.</p><h2>Time isn&#8217;t one thing</h2><p>Once I started paying attention, I learned about just how differently people seem to experience time. Some really do see the blocks, time as a grid to fill. Others feel it as a current: sometimes rushing, sometimes pooling. Some experience it cyclically, looping back through familiar rhythms. Others feel it always running out. And some live mostly in the eternal now.</p><p>Each of these perceptions creates a different relationship to pressure, prioritisation, and momentum. And yet, most productivity systems are built around a single assumption: that time is fixed, linear, and schedule-shaped. That assumption alone can leave people feeling behind, overwhelmed, or strangely out of sync &#8212; not because they&#8217;re disorganized, but because the tool they&#8217;re using doesn&#8217;t match the way they relate to time.</p><h2>The tools that shape our time</h2><p>It&#8217;s not just that most systems assume time is linear &#8212; it&#8217;s that they quietly teach us to operate as if that&#8217;s the only valid shape it can take. Planning tools, digital calendars, and habit trackers rarely accommodate how elastic time can feel. Instead, they ask us to pin things down in advance: block out hours, assign slots, colour-code priorities. They promise structure &#8212; and sometimes they deliver it &#8212; but only if you already think in blocks and respond well to grids.</p><p>For those whose experience of time is more fluid or inconsistent, this can create a strange kind of friction. You&#8217;re not just managing your tasks, you&#8217;re performing a version of yourself who fits into the structure the tool expects. It becomes easy to mistake falling out of sync with the system as a personal failure, rather than a sign the system might be too rigid.</p><p>That rigidity isn&#8217;t always overt. It&#8217;s in the quiet expectation that focus can be scheduled. That motivation will arrive on time. That life won&#8217;t interrupt. When those things don&#8217;t hold true &#8212; and often they don&#8217;t &#8212; it can leave you feeling like the problem is you, not the framework you&#8217;ve been handed.</p><h2><strong>A different kind of rhythm</strong></h2><p>By now it&#8217;s clear: time doesn&#8217;t always behave. It stretches, shrinks, gets interrupted. Our energy ebbs and flows. Plans rarely hold. And yet, many systems still expect us to treat time like a fixed asset, divisible, predictable, tightly bound.</p><p>Over time, I&#8217;ve come to value systems that don&#8217;t expect me to be consistent in form, only in attention. Systems that let the shape of my focus change from one day to the next, that don&#8217;t require me to guess in advance when energy will arrive or how long it will last. Ones that hold space for ambiguity, without collapsing under it. I&#8217;ve noticed that what makes a system feel good isn&#8217;t how much it lets me get done, but how well it adapts when things inevitably shift.</p><p>That&#8217;s the quiet logic behind Spontaneous Productivity &#8212; not to eliminate structure, but to stop trying to impose the wrong kind. Instead of treating time as a container to be filled, it invites you to see it as something you&#8217;re in a relationship with &#8212; responsive, alive, unpredictable, and often surprising. It asks: what if your productivity system didn&#8217;t try to fix time, but moved with it?</p><p>This is what Nestful is designed to support &#8212; not because it has every feature under the sun, but because it doesn&#8217;t ask you to perform a version of yourself that never misses a beat. It lets you log what matters, then step away. And when you return, it quietly surfaces the things that are still to do. Some days you might work in sharp, focused bursts. Other days, you might drift, returning to tasks in fragments. Nestful doesn&#8217;t reprimand either. It simply keeps hold of the thread until you&#8217;re ready to pick it back up.</p><p>Maybe the real work isn&#8217;t to master time, but to recognize its many shapes &#8212; and find ways to move within them that feel less like resistance, more like rhythm. Not every hour needs to be optimized. Not every task needs a slot. Sometimes, just knowing the thread is still there is enough.</p><p>Try <a href="https://nestful.app/?utm_source=substack&amp;utm_medium=blog&amp;utm_campaign=myth">Nestful</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/subscribe?"><span>Subscribe now</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/p/what-productivity-systems-often-get?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/p/what-productivity-systems-often-get?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[The power of consistent habits (without fixed timing)]]></title><description><![CDATA[A while back, I tried to lock in the perfect morning routine.]]></description><link>https://blog.nestful.app/p/the-power-of-consistent-habits-without</link><guid isPermaLink="false">https://blog.nestful.app/p/the-power-of-consistent-habits-without</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Mon, 26 May 2025 13:27:10 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7jZs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c63381-45e6-4908-82d4-ca470e006899_1590x774.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A while back, I tried to lock in the perfect morning routine. The logic was simple: if I could create a repeatable structure &#8212; wake up, gym at 6:30 AM, breakfast at 7:30, deep work at 8 &#8212; I&#8217;d become a productivity machine. The problem? My days refused to cooperate. Some mornings I slept poorly, some days I had early meetings, and occasionally I just didn&#8217;t feel like following the script. Each time I missed a scheduled gym session, it threw off my whole day. The mental overhead of constantly adjusting my calendar was exhausting. Instead of feeling productive, I felt trapped.</p><p>If you&#8217;ve ever spent more time moving things around in your planner than actually doing them, you know what I mean. Rigid scheduling assumes a level of control over life that simply doesn&#8217;t exist. Meetings get moved, energy fluctuates, and priorities shift. The more precisely you plan, the more you have to fight reality to make those plans work.</p><p>But here&#8217;s the thing: I never stopped working out. I just stopped trying to schedule it.</p><h2>Energy matters more than the clock</h2><p>Instead of saying, "I will go to the gym at 6:30 AM every day," I switched to a simpler rule: "I will go to the gym on Monday, Wednesday and Saturday.&#8221; No fixed time, no strict routine. I decided I could commit to consistency but not precision. Some days I go at 6:30 AM, some days at 10:30 AM. The time varies based on how I feel, what my workload looks like, or whether the class I want to take is full. But because the habit itself is solid, it happens regardless of when I do it.</p><p>This shift in mindset &#8212; prioritizing consistency over scheduling &#8212; was a game-changer. It turns out, you don&#8217;t need rigid routines; you need reliable habits. The real trick isn&#8217;t forcing yourself to do something at an exact time, it&#8217;s making sure you do it at all.</p><p>That&#8217;s where the real power lies: not in perfect timing, but in dependable rhythm. It&#8217;s a quieter kind of discipline &#8212; one that doesn&#8217;t crack when life throws you a curveball. Some mornings I still wake up groggy. Sometimes meetings spill over, or I just want to read in bed. But none of that derails the habit. I know the workout will happen &#8212; not because it's locked in at 6:30 AM, but because it's part of the rhythm now.</p><p>I used to think freedom meant being flaky. That if I wasn&#8217;t militant about scheduling, everything would fall apart. But what I&#8217;ve found &#8212; and maybe you&#8217;ve felt it too &#8212; is that freedom paired with clarity is a far more sustainable motivator than control paired with guilt. That&#8217;s what makes systems like <a href="https://dontbreakthechain.com/">Seinfeld&#8217;s &#8220;Don&#8217;t Break the Chain&#8221;</a> so sticky &#8212; not because they&#8217;re strict, but because they reward consistency without boxing you in. When the stakes are &#8220;do it today&#8221; instead of &#8220;do it by 9:00 sharp,&#8221; you&#8217;re more likely to follow through and feel good about it.</p><p>This is where <a href="https://blog.nestful.app/about">Spontaneous Productivity</a> stopped being an idea and started becoming something I could actually live by. I didn&#8217;t need a perfect routine &#8212; I needed help knowing what to focus on next, without overthinking it. That&#8217;s what Nestful gave me. It didn&#8217;t shame me for being &#8220;off schedule.&#8221; It just surfaced what mattered &#8212; that HIIT class, that blog draft, that overdue call &#8212; and let me decide <em>when</em> to tackle it. No calendar Tetris. No mental drag.</p><p>Here&#8217;s what I&#8217;ve got recurring in Nestful right now:</p><ul><li><p>Monday / Wednesday / Saturday: &#8220;Move your body&#8221; (I choose the format)</p></li><li><p>Daily: &#8220;Write something, even a sentence&#8221; (keeps me creatively in motion)</p></li><li><p>Weekly: &#8220;Inbox zero-ish&#8221; (emphasis on the -ish)</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7jZs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c63381-45e6-4908-82d4-ca470e006899_1590x774.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7jZs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c63381-45e6-4908-82d4-ca470e006899_1590x774.png 424w, https://substackcdn.com/image/fetch/$s_!7jZs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c63381-45e6-4908-82d4-ca470e006899_1590x774.png 848w, https://substackcdn.com/image/fetch/$s_!7jZs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c63381-45e6-4908-82d4-ca470e006899_1590x774.png 1272w, https://substackcdn.com/image/fetch/$s_!7jZs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c63381-45e6-4908-82d4-ca470e006899_1590x774.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7jZs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c63381-45e6-4908-82d4-ca470e006899_1590x774.png" width="1456" height="709" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d0c63381-45e6-4908-82d4-ca470e006899_1590x774.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:709,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7jZs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c63381-45e6-4908-82d4-ca470e006899_1590x774.png 424w, https://substackcdn.com/image/fetch/$s_!7jZs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c63381-45e6-4908-82d4-ca470e006899_1590x774.png 848w, https://substackcdn.com/image/fetch/$s_!7jZs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c63381-45e6-4908-82d4-ca470e006899_1590x774.png 1272w, https://substackcdn.com/image/fetch/$s_!7jZs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd0c63381-45e6-4908-82d4-ca470e006899_1590x774.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>These aren&#8217;t rigid time slots. They&#8217;re rhythms. I don&#8217;t always hit them perfectly, but I rarely miss them entirely &#8212; and that&#8217;s what counts.</p><p>The rhythm itself is flexible. What starts as &#8220;Monday, Wednesday, Saturday&#8221; might shift to Tuesday or Sunday one week &#8212; and that&#8217;s fine. The point isn&#8217;t to hit every beat exactly, but to keep the beat going. Say I usually work out every other day. If Friday gets wiped out by a last-minute wedding or bad sleep, I don&#8217;t panic and &#8220;catch up&#8221; &#8212; I just don&#8217;t tick the task. The next time I check it off, the rhythm continues from there. It doesn&#8217;t punish me. It flows. That&#8217;s the beauty of it: the interval stays intact, even when the exact days shift.<br><br>You don&#8217;t fall off the wagon; you just pick it back up at the next step. That&#8217;s what makes this approach so forgiving &#8212; and so effective. It meets you where you are, and it keeps going when you&#8217;re ready again.</p><p>I think we&#8217;ve been sold this myth that real discipline has to look militant &#8212; same time, same sequence, every day. But in reality, the people who get the most done aren&#8217;t always the ones with the tightest schedules. They&#8217;re the ones with systems that move with them &#8212; who focus less on planning the day and more on showing up for what matters.</p><p>That&#8217;s what Nestful helps me do: not just stay on track, but redefine what the track even looks like. It lets me show up &#8212; imperfectly, but consistently &#8212; and that&#8217;s enough.</p><p>Try <a href="https://nestful.app/?utm_source=substack&amp;utm_medium=blog&amp;utm_campaign=myth">Nestful</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/subscribe?"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[How Nestful syncs without a backend, plus a mindset shift for better work]]></title><description><![CDATA[Explore the offline-first architecture behind Nestful&#8217;s sync system, and a reframe that turns productivity into play]]></description><link>https://blog.nestful.app/p/how-nestful-syncs-without-a-backend</link><guid isPermaLink="false">https://blog.nestful.app/p/how-nestful-syncs-without-a-backend</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Wed, 16 Apr 2025 12:04:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!aZOI!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13336315-f7a5-451b-ae0a-2cf7a1f81850_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Two posts this week - one technical, one personal - but both grounded in the same principle: making things work better with less effort.</p><p>The first is a deep dive into how Nestful pulls off offline-first syncing using CRDTs, Yjs, and object storage, without relying on a traditional backend. It&#8217;s a practical look at keeping data consistent across devices while staying lightweight and low-maintenance.</p><p>The second is a reflection on what happens when productivity stops feeling like a chore. By approaching tasks with curiosity and playfulness instead of pressure, it becomes easier to get into flow and actually enjoy the work.</p><p>For anyone building tools or habits that reduce friction and invite momentum, both posts might resonate.</p><div><hr></div><h2>How Nestful uses CRDTs to sync your data with (almost) no backend</h2><p>Nestful has quite the interesting use case &#8211; it is an offline first app, yet it supports cross-device sync. This means it should somehow consolidate data even if it&#8217;s the result of multiple different offline sessions coming back online at different times.</p><p>Most of you are already aware that the most common way to do this today is Conflict-free Replicated Data Types (CRDTs). As their name suggests, they are replicated data structures that can be combined, with algorithms guaranteeing that the data will eventually converge.</p><p>Nestful uses Yjs, a fast JavaScript CRDT. Yjs is truly a magnificent piece of software. Each user has its own Yjs Document where most data is stored. For local persistence that data is synced into IndexedDB, the in-browser database, using y-indexeddb, which is part of Yjs&#8217; <em>provider</em> ecosystem.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/p/how-nestful-uses-crdts-to-sync-your&quot;,&quot;text&quot;:&quot;Continue reading&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/p/how-nestful-uses-crdts-to-sync-your"><span>Continue reading</span></a></p><div><hr></div><h2>How to reframe productivity as play, not work</h2><p>Last week I turned avoiding my quarterly content calendar into an Olympic sport. The task sat there in my to-do app, glaring at me. Instead of starting, I spent 30 minutes reorganizing my task list. Classic avoidance behavior.</p><p>It wasn't until I reframed the problem &#8211;"let's experiment with content themes that might resonate" &#8211; that I finally dove in. Three hours disappeared in a flash. The difference? I'd accidentally turned work into play.</p><h3>Why productivity feels like a chore</h3><p>You know the feeling when you open your task manager and a long list (of things you don&#8217;t want to do) stares back at you. <em>Where do I start? What's most important? Why does this feel overwhelming before I even begin?</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/p/how-to-reframe-productivity-as-play&quot;,&quot;text&quot;:&quot;Continue reading&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/p/how-to-reframe-productivity-as-play"><span>Continue reading</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://nestful.app/&quot;,&quot;text&quot;:&quot;Try Nestful&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://nestful.app/"><span>Try Nestful</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Spontaneous Productivity! Subscribe for free to receive new posts and support our work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How to reframe productivity as play, not work]]></title><description><![CDATA[The joy of doing]]></description><link>https://blog.nestful.app/p/how-to-reframe-productivity-as-play</link><guid isPermaLink="false">https://blog.nestful.app/p/how-to-reframe-productivity-as-play</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Mon, 07 Apr 2025 16:50:14 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!aZOI!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13336315-f7a5-451b-ae0a-2cf7a1f81850_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last week I turned avoiding my quarterly content calendar into an Olympic sport. The task sat there in my to-do app, glaring at me. Instead of starting, I spent 30 minutes reorganizing my task list. Classic avoidance behavior.</p><p>It wasn't until I reframed the problem &#8211;"let's experiment with content themes that might resonate" &#8211; that I finally dove in. Three hours disappeared in a flash. The difference? I'd accidentally turned work into play.</p><h2><strong>Why productivity feels like a chore</strong></h2><p>You know the feeling when you open your task manager and a long list (of things you don&#8217;t want to do) stares back at you. <em>Where do I start? What's most important? Why does this feel overwhelming before I even begin?</em></p><p>The trouble is that most to-do lists don't help you focus. They present everything at once, leaving your brain stuck in decision mode rather than execution mode. Organizing tasks can feel productive, but too often, it&#8217;s a trap. I&#8217;ve lost entire mornings tweaking timeblocks, only for my boss to call with an urgent request that upends everything.</p><p>After cycling through probably a dozen productivity systems over the years, I've realized something: the best work happens when I&#8217;m engaged, curious, and free to follow momentum. Productivity isn't a chore when it feels like play.</p><h2><strong>How to reframe work as play</strong></h2><p>Play isn&#8217;t the opposite of work; it&#8217;s how we solve problems most effectively. Mark Twain captured this idea in Tom Sawyer, tricking his friends into whitewashing a fence by making it seem fun. The work stayed the same; the framing shifted.</p><p>Research backs this up. Mih&#225;ly Cs&#237;kszentmih&#225;lyi&#8217;s work on flow states shows that when tasks provide the right balance of challenge and skill, people enter a deeply focused, high-performance state. Studies by Teresa Amabile and Edward Deci have found that intrinsic motivation &#8212; genuine interest rather than external rewards &#8212; leads to better creativity and productivity. That&#8217;s why Google&#8217;s 20% time (which led to Gmail) and IDEO&#8217;s play-jams work: they invite curiosity and experimentation, making work feel effortless.</p><p>The shift happens when you stop seeing tasks as obligations and start seeing them as interesting problems to solve. So here&#8217;s my attempt:</p><ul><li><p>Writing becomes exploration rather than obligation when I approach it with curiosity.</p></li><li><p>Content planning turns into strategic storytelling rather than filling slots on a calendar.</p></li><li><p>Analytics reviews can be either tedious number-crunching or fascinating detective work.</p></li></ul><p>The trick isn't forcing yourself to work harder &#8212; it's shifting how you experience the work. When I catch myself dreading a task, I ask: "How can I make this interesting?"</p><p>I get it &#8212; sometimes work feels like an endless slog, especially if you&#8217;re stuck in a role that seems inherently unexciting. Maybe you&#8217;re processing invoices, entering data, or sitting through mind-numbing meetings. My examples &#8212; writing, content planning, analytics &#8212; might sound creative or dynamic compared to that. But here&#8217;s the thing: the magic of reframing isn&#8217;t about the task itself, it&#8217;s about how you approach it. Even the most &#8220;tedious&#8221; work can become a playground if you tweak your lens.</p><h2><strong>Practical ways to reframe work as play</strong></h2><p>Here's what actually works for me when turning tedious tasks into engaging activities:</p><ol><li><p><strong>Make a mess first. <br></strong>Play is messy. When I'm stuck in perfectionism, I'll deliberately create an "ugly first draft" where the goal is speed, not quality. It's liberating to intentionally write something bad, knowing you'll improve it later. The fear of imperfection kills play. Give yourself permission to make a mess first.</p></li><li><p><strong>Create challenges, not chores.<br></strong>I used to dread the monthly analytics reviews until I reframed them as detective work: "Can I discover one surprising insight nobody else has noticed?" Suddenly I was hunting for clues instead of trudging through data. Ask yourself: "What's the puzzle here that I'm trying to solve?" Almost any task can be reframed as an interesting challenge.</p></li><li><p><strong>Lower the stakes with experiments.<br></strong>When I frame tasks as experiments rather than obligations, my fear of failure disappears. A recent email campaign flopped, but because I'd defined it as "an experiment in storytelling formats," I analyzed the results with curiosity instead of disappointment. Try asking: "What if this were just a prototype I'm testing?" The mental shift is immediate.</p></li><li><p><strong>Build feedback loops.<br></strong>Games are addictive because they provide instant feedback. I've started breaking content projects into 25-minute chunks with clear "win conditions." Instead of "work on blog post," I'll set a goal like "write three compelling hooks and pick the best one." After each sprint, I get that small dopamine hit of accomplishment. Even better: share early drafts with colleagues and get quick reactions. Their feedback creates natural momentum.</p></li></ol><h2><strong>How play fits into my productivity system</strong></h2><p>If there's one thing I&#8217;ve learned, it&#8217;s that forcing productivity rarely works. The best work happens when we tap into curiosity, momentum, and engagement &#8212; when work feels like play. But most productivity tools aren&#8217;t designed with that in mind. They assume we thrive on strict schedules and rigid planning when, in reality, that approach often leads to burnout, avoidance, and endless reshuffling.</p><p>What if, instead of wrestling with productivity systems, we used tools that worked the way our brains naturally do &#8212; surfacing what&#8217;s important at the right moment without constant effort? That&#8217;s the idea behind spontaneous productivity &#8212; a workflow that prioritizes action over overthinking.</p><p>That&#8217;s the kind of system I&#8217;ve gravitated toward. I need something that helps me keep up momentum without getting stuck in decision loops or spending half my time reorganizing a to-do list. Lately, I&#8217;ve been using Nestful because it fits that mindset: it doesn&#8217;t force rigid plans but makes sure the right things rise to the surface when I need them. Less managing, more doing. And for me, that makes all the difference.</p><p>Try <a href="https://nestful.app/?utm_source=substack&amp;utm_medium=blog&amp;utm_campaign=play">Nestful</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/subscribe?"><span>Subscribe now</span></a></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[How Nestful uses CRDTs to sync your data with (almost) no backend]]></title><description><![CDATA[Nestful has quite the interesting use case &#8211; it is an offline first app, yet it supports cross-device sync.]]></description><link>https://blog.nestful.app/p/how-nestful-uses-crdts-to-sync-your</link><guid isPermaLink="false">https://blog.nestful.app/p/how-nestful-uses-crdts-to-sync-your</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Mon, 07 Apr 2025 16:48:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!cz__!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F153561d2-2979-422a-a437-163a9cb72881_1448x1192.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Nestful has quite the interesting use case &#8211; it is an offline first app, yet it supports cross-device sync. This means it should somehow consolidate data even if it&#8217;s the result of multiple different offline sessions coming back online at different times.</p><p>Most of you are already aware that the most common way to do this today is Conflict-free Replicated Data Types (CRDTs). As their name suggests, they are replicated data structures that can be combined, with algorithms guaranteeing that the data will eventually converge.</p><p>Nestful uses Yjs, a fast JavaScript CRDT. Yjs is truly a magnificent piece of software. Each user has its own Yjs Document where most data is stored. For local persistence that data is synced into IndexedDB, the in-browser database, using y-indexeddb, which is part of Yjs&#8217; <em>provider</em> ecosystem.</p><p>Providers are pluggable pieces of code that hook into a Yjs document for added functionality. This is mostly in the form of sync to and from various places, from our local IndexedDB database to a middle ground in the form of y-websocket, to a full-blown solution like y-sweet.</p><p>Although I would have been glad to find a ready-made provider and integrate that into Nestful, things were not so easy. Commercial providers like y-sweet were in their infancy, so I didn&#8217;t even bother evaluating them. The most battle-tested solution was y-websocket, but that was just the communication layer. I would have had to write a non-negligible amount of backend code, which development (and maintenance) bandwidth did not allow for.</p><p>What do we do, then? To understand our options we need to figure out where Nestful was at the time, and what it is that we need to sync.</p><h2>Nestful&#8217;s legacy</h2><p>Historically Nestful was written as a generic, simple CRUD app using PostgreSQL via Supabase. I thought to myself &#8211; it&#8217;s a personal-use todo app, why would I need anything else? I&#8217;d say that for most todo apps that decision would have been correct (and it surely was correct at the time, for other reasons). The thing is, Nestful has two very important traits that change most of everything:</p><ol><li><p>Nestful is offline-first, which now requires constructing a sync mechanism</p></li><li><p>Nestful uses a tree-structure which the traversal of can cause significant recursion</p></li></ol><p>Don&#8217;t let anyone tell you otherwise &#8211; both are perfectly solvable and maintainable using PostgreSQL, even if they lend themselves better to something like a document-based CRDT.</p><p>Nestful&#8217;s other circumstances, including the way it was coded and my annoyance with my then local DB (a story for another time about how not to maintain FOSS) pushed me to switch.</p><p>So now we know that Nestful is hosted on Supabase, relying on its Auth and its Database, and most important of all &#8211; Nestful does not deploy any backend. This means that adding one will not only incur the complexity of the functionality we want to add, but of the expanded codebase, its deployment, and the server maintenance.</p><p>Doing that is all well and good, we are a software company after all, but let&#8217;s do better.</p><h2>Update here, update there</h2><p>That way sync works with Yjs (and most other CRDTs) is by using an update mechanism. Yjs allows the developer to obtain binary updates, representing a data diff, then send those over the wire. There are 3 main ways to do that:</p><ol><li><p>Listen to changes</p></li><li><p>Diff against another version of the data</p></li><li><p>Get everything</p></li></ol><p>Those updates can then be consumed in any order and they will converge to the same final state. Now that we know we want to sync updates, the questions are where do we save them and how will they get there.</p><p>Remember Nestful using Supabase? Well, Supabase has an S3 equivalent called Supabase Storage. The other side of the coin of provider lock-in is of course, blissful integration. After a minimal setting of permissions, end users would be able to push updates to Storage, and fetch updates that are already there.</p><p>Of course, just fetching all the updates all the time can be wasteful (and slow), so we&#8217;ll have to do some tweaking.</p><h2>Almost converged</h2><p>To not have the client download a humongous amount of files, which is as slow over these &#8220;serverless&#8221; platforms almost as how stupid the term &#8220;serverless&#8221; is, we could write a small &#8220;serverless&#8221; function to consolidate the updates for us.</p><p>That function will receive a date from the client, and will send over a single file.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cz__!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F153561d2-2979-422a-a437-163a9cb72881_1448x1192.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cz__!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F153561d2-2979-422a-a437-163a9cb72881_1448x1192.png 424w, https://substackcdn.com/image/fetch/$s_!cz__!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F153561d2-2979-422a-a437-163a9cb72881_1448x1192.png 848w, https://substackcdn.com/image/fetch/$s_!cz__!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F153561d2-2979-422a-a437-163a9cb72881_1448x1192.png 1272w, https://substackcdn.com/image/fetch/$s_!cz__!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F153561d2-2979-422a-a437-163a9cb72881_1448x1192.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cz__!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F153561d2-2979-422a-a437-163a9cb72881_1448x1192.png" width="1448" height="1192" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/153561d2-2979-422a-a437-163a9cb72881_1448x1192.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1192,&quot;width&quot;:1448,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cz__!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F153561d2-2979-422a-a437-163a9cb72881_1448x1192.png 424w, https://substackcdn.com/image/fetch/$s_!cz__!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F153561d2-2979-422a-a437-163a9cb72881_1448x1192.png 848w, https://substackcdn.com/image/fetch/$s_!cz__!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F153561d2-2979-422a-a437-163a9cb72881_1448x1192.png 1272w, https://substackcdn.com/image/fetch/$s_!cz__!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F153561d2-2979-422a-a437-163a9cb72881_1448x1192.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When a client wants to get the latest data, he sends a date to the just-a-function-not-a-server. Upon receiving the combined update, the client diffs its own state against it, and uploads a diff directly to the just-object-storage-not-a-server.</p><p>This is called a full sync. At this point, the client and server are fully in sync. Any further update on the client is:</p><ol><li><p>Uploaded directly to object storage</p></li><li><p>Broadcasted to peers using Supabase, to avoid a roundtrip</p></li></ol><p>Using this scheme also has the added benefit of being able to restore data to a point in time, even if it was garbage collected by Yjs.</p><p>And that&#8217;s how Nestful syncs your data. This simple, client-dependent approach serves us well. It works, is stable, low maintenance, but&#8230; it&#8217;s slow. Nestful does <em>not</em> in fact require fast sync. It is not a real-time collaborative platform like other products using Yjs, and so sync can happen in the background, take a few seconds, and no one would notice.</p><p>But&#8230; we can&#8217;t settle for that, can we?</p><h2>The future ahead</h2><p>Even though it is fine for now (and for a long time from now) that a full sync is slow, we may need a bit more performance in the future. Although this may require getting an actual server and setting up an actual backend, the current design will stay mostly the same.</p><p>Since end-to-end encryption is planned for Nestful, the server will not be able to consolidate updates. Instead, a client will have to do that periodically and upload an encrypted checkpoint, which will serve as the new starting point for the next consolidation.</p><p>For this we&#8217;re going to need to cache both the metadata and data since the latest checkpoint for the update files in object storage to be able to quickly and efficiently serve whatever is needed, but that is for the future.</p><p>Check out the marvel of CRDT syncing by <a href="https://nestful.app/signup">trying Nestful</a> now, and also try Nestful itself while you&#8217;re at it.</p><p>Frequent readers will remember Nestful is written using Gleam and will probably wonder how we call Yjs from inside our lovely Gleam code. Here&#8217;s a <a href="https://hexdocs.pm/ygleam/">YGleam link</a> for you.</p><p>You may also be interested in <a href="https://blog.nestful.app/p/its-tea-time-nestful-now-manages">our latest story about moving to an Elm-like state management</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/subscribe?"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[It's TEA time: Nestful now manages state with Gleam and Lustre]]></title><description><![CDATA[Does a frontend framework matter?]]></description><link>https://blog.nestful.app/p/its-tea-time-nestful-now-manages</link><guid isPermaLink="false">https://blog.nestful.app/p/its-tea-time-nestful-now-manages</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Mon, 31 Mar 2025 17:07:41 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fcbdda7c-b8dc-44c1-97e1-ee7ac6f36091_900x630.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Does a frontend framework matter?</p><p>As much as you might have heard otherwise, the answer is an astounding yes. Those who fight on the hills pretending it&#8217;s all about the code are missing one very crucial point:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Spontaneous Productivity ! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Frameworks shape the code you write.</p><p>To a certain extent, they define what&#8217;s possible, but more important than that &#8211; they set a path of least resistance. Yes, frameworks coming and going is one of the most frequent occurrences in nature, but that doesn&#8217;t mean it&#8217;s wasted effort to choose the right one or invest in it. The waste only appears when the frameworks are of the same kind, and introduce similar tradeoffs.</p><p>The clearest example of this is Elm. It&#8217;s not a coincidence that Elm manages state differently than how you&#8217;d normally do it in JS-land. Even Redux, which is an implementation of The Elm Architecture (also known as Model-View-Update), could not give the same streamlined experiences developers expect from Elm. Even if that experience is better than what&#8217;s otherwise available in the JavaScript ecosystem, you have to be fairly disciplined to keep it going.</p><p>Alas, there is only so much time in this world for us to try and fit a square into a peg. At this point some would fall back to the usual default: &#8220;Elm is great, but it&#8217;s too far from JavaScript. And there&#8217;s really nothing very different about all the JavaScript options, so I&#8217;ll just &#8211; &#8221;</p><p>But wait. What if I told you there is another option? As robust as Elm, with a familiar syntax, and that you could learn in a day?</p><p>Grab yourself some tea, for this is the story of how adopting the Gleam language out of pure disdain for TypeScript led to much better state management for Nestful.</p><p>Before we delve into the details, let me bring you up to speed.</p><p>About a year since I started experimenting with <a href="https://gleam.run">Gleam</a>, a delightful functional language that compiles to both Javascript and Erlang. I&#8217;ve decided to rewrite Nestful in Gleam to alleviate <a href="https://blog.nestful.app/p/why-i-rewrote-nestful-in-gleam">some of the terrible slog</a> that is TypeScript.</p><p>The first major part was the introduction of <a href="https://github.com/vleam/vleam">Vleam</a>, a set of tools that allows one to write Gleam inside Vue SFCs. Nestful is written with Vue and incremental adoption is a must when working on such a large-scale rewrite.</p><p>In November I launched <a href="https://blog.nestful.app/p/gleams-lustre-is-frontend-developments">OmniMessage</a>, an experimental library for client-server communication. OmniMessage, you see, is an extension of Lustre (please bear with the buzzwords), Gleam&#8217;s Elm-inspired frontend framework. It allows one to define messages that are common to both the client and the server, thus streamlining communication.</p><p>Whether that&#8217;s a good idea or not, time will tell. What I do know is that Lustre was a breath of fresh air. But why? What is it about Lustre that&#8217;s missing from Redux (except for my strong, deep, unwavering dislike of React, that is)?</p><p>The answer is Gleam. Writing MVU-style apps in JavaScript is a bit like driving in a zigzag pattern &#8212; possible, but unless you&#8217;re 100% diligent, a crash is coming.</p><p>Luckily, I try to avoid premature optimization as much as possible and state management was no different. Nestful&#8217;s was simply done, by hooking up different kinds of Vue composables and utilizing Yjs&#8217; event system. Some people may gasp at the lack of structure, but hindsight has revealed this to be a good decision. Much better than committing to what some would consider &#8220;proper&#8221; state management, spreading its tentacles into every corner of Nestful, making change a labor of desperation.</p><p>Instead, having a simple, unstructured system allowed it to be promptly removed when the learnings were sufficient and time had come to replace it.</p><p>After adopting Gleam for the language it is, Nestful could continue to enjoy its ecosystem, including Elm-inspired Lustre.</p><h2>Why you should have some TEA</h2><p>The Elm Architecture (TEA), also known as Model-View-Update (MVU), is a centralized form of state management made popular by &#8212;you guessed it &#8212;The Elm programming language.</p><p>As its alternative name suggests, it involves a centralized model that is sent for rendering to a view function, and is updated by an update function. If it sounds simple, that&#8217;s because it is. Here&#8217;s the state management diagram straight out of Elm&#8217;s docs:<br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qh51!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qh51!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png 424w, https://substackcdn.com/image/fetch/$s_!qh51!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png 848w, https://substackcdn.com/image/fetch/$s_!qh51!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png 1272w, https://substackcdn.com/image/fetch/$s_!qh51!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qh51!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png" width="1456" height="920" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/be0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:920,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:160157,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.nestful.app/i/160270568?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qh51!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png 424w, https://substackcdn.com/image/fetch/$s_!qh51!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png 848w, https://substackcdn.com/image/fetch/$s_!qh51!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png 1272w, https://substackcdn.com/image/fetch/$s_!qh51!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe0189ec-2ba9-484b-88f8-abab3227ef8f_2500x1580.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Here&#8217;s the counter example from Lustre, Gleam&#8217;s frontend framework, which also implements TEA:</p><pre><code>type Msg {

  Incr  

  Decr

}



fn update(model, msg) {

  case msg {

    Incr -&gt; model + 1

    Decr -&gt; model - 1

  }

}



fn view(model) {

  let count = int.to_string(model)

  div([], [

    button([on_click(Incr)], [text(" + ")]),

    p([], [text(count)]),

    button([on_click(Decr)], [text(" - ")])

  ])

}</code></pre><p></p><p>That&#8217;s it, kids. For most web applications, you can pack Pinia and Zustand up, thank them for their service, and promptly show them out to the nearest commit.</p><p>You see, those popular solutions are popular for good reason. They are a low common denominator that, with all honesty, works quite well. The problem is they&#8217;re not as good as it gets. They break at the edges, if you will. A local maxima.</p><p>For Nestful, The Elm Architecture has the edge on them in three crucial places.</p><h3>Preventing state-creep</h3><p>State creep is the bane of my existence. When components are stateful, one of two things must happen:</p><ol><li><p>You must hold constant, unwavering diligence, keeping local state local and global state global.</p></li><li><p>You despair as global and local states are inevitably mixed into a soup of misery.</p></li></ol><p>I personally am not a fan of either constantly looking over my shoulder for the state boogeyman, nor of holding the mental model the size of the Tokyo rail system of how state flows through an application.</p><p>Instead, using TEA, I have a lovely, detailed map of all the paths data flows in. That map is an entire overview of the application, caging in the global state, only to be seen &#8212; never mutated.</p><h3>A pure heart</h3><p>Another great advantage of strictly enforcing TEA is that it makes it very easy to structure an application using &#8220;Functional Core, Imperative Shell&#8221;, a design pattern in which I/O is separated out to distinct parts at the start and end of a flow, while the center, the business logic processing that I/O, is kept pure. It always returns the same output for a given input, and does not involve side effects.</p><p>As it so happens, that is exactly how TEA pushes you to architect your state. Both the update and view functions are pure, and will return the same output for a given model and message.</p><p>Side effects are then handled by an effect system, which takes side-effect tasks returned by the update function and executes them out of the update function&#8217;s context. <br><br>This approach isolates the important bit of the application, the business logic, from the ever-changing, hardly testable, outside world.</p><h3>Works great with Gleam</h3><p>Gleam lends itself to this model quite significantly. Being a functional, type-safe language is a great usability gain for TEA, especially thanks to discriminate unions, who allow us to write the apps message type like this:<br></p><pre><code>type Msg {

  Incr

  Decr

}</code></pre><p>And the case statement, that allows for exhaustive matching on that message type:<br></p><pre><code>fn update(model, msg) {

  case msg {

    Incr -&gt; model + 1

    Decr -&gt; model - 1

  }

}</code></pre><p>Other implementations, like Redux, Dartea, or Vuex to some extent, while admirable, all fall short. After a developer has chosen a paradigm, tools need to not only enforce it, but encourage that paradigm. They need to not only make it the path of least resistance, but make the other paths hard in comparison.</p><p>A tool that requires diligence to keep going is failing at its job.</p><p>An added bonus from those three advantages is they make it extremely easy to refactor. They allow razing fields of old code to the ground, building skyscrapers above them, exchanging the fear of imminent crumble for the guarantees of type-safety and robust defaults.</p><p>But wait, isn&#8217;t Nestful a Vue app? How am I going to fit Gleam-written TEA into it? Am I going to have to settle for a TEA-like experience, similar to what I could conjure up using existing TypeScript solutions?</p><h2>Incrementally adopting a frontend framework</h2><p>So I&#8217;ve adopted Gleam and can now write it inside Vue files instead of TypeScript and look to use it to implement TEA and replace the current haphazardly-managed state.</p><p>To do that, two main tasks need to be completed:</p><ol><li><p>Construct a runtime capable of executing the TEA state machine.</p></li><li><p>Somehow integrate that runtime into existing Gleam and TypeScript Vue components.</p></li></ol><p>The first one already exists, it&#8217;s just Lustre (but without a view).</p><p>There are two ways of going about incorporating Lustre incrementally. The first is to carve out portions of Nestful and write those exclusively in Lustre. The second is to launch a Lustre app and render nothing, using just its state management capabilities.</p><p>Since the goal here is to have comprehensive state management, the second solution is more appealing, but a closer look will reveal that it&#8217;s actually the only solution possible.</p><p>Nestful, you see, is a giant item graph that&#8217;s traversed for specific views or actions. To enable offline-only capabilities, the entirety of the graph is stored on the client in the form of a single Yjs document, and if enabled, is synced on changes.</p><p>This means that not only is there very little local state to manage, the global nature of Nestful&#8217;s state will greatly benefit from collecting the static logic into a single, coherent, state machine. For Nestful, it makes much more sense to embed Vue in Lustre rather than Lustre in Vue.</p><p></p><pre><code>// /src/compositions/useNestful.ts

import { computed, shallowRef, watch } from 'vue';

import { UseNestful } from '../nestful/compositions.gleam';

import { nestful } from '../nestful.gleam';



const modelRef = shallowRef();

const modelComputed = computed(() =&gt; modelRef.value);



const [initialModel, dispatch] = nestful((model) =&gt; {

  modelRef.value = model;

});



modelRef.value = initialModel;



export function useNestful() {

  return new UseNestful(modelComputed, dispatch);

}





// /src/nestful/compositions.gleampub type UseNestful {

  UseNestful(model: vue.Computed(Model), dispatch: fn(Msg) -&gt; Nil)

}



@external(javascript, "/src/compositions/useNestful", "useNestful")

pub fn use_nestful() -&gt; UseNestful</code></pre><p></p><p>This is the glue code that allows Vue components, written either in Gleam or TypeScript, to read the model and dispatch messages. It is supported by helper functions in nestful.gleam to avoid having to import the different Gleam types into TypeScript.</p><p>Vue components are then treated as functions composing a Lustre app&#8217;s view function.</p><p>But there&#8217;s a catch.</p><p>The Elm Architecture&#8217;s centrally managed state trades off component composability to the point where Elm&#8217;s docs <a href="https://guide.elm-lang.org/webapps/structure">outright discourage them</a>. While I don&#8217;t consider this blasphemy like some React folks may, I can see how different parts of an app lose on not being completely separate.</p><p>All things considered, it&#8217;s not an endemic issue similar to those I am trying to solve with the move to Gleam. That same page on the Elm docs discouraging components lists much more important things right before that that are benefits of TEA.</p><p>Which brings us to today.</p><h2>A fun future</h2><p>Except for two pending files, no global state is managed outside Lustre. State holding composables have been reduced significantly, and I hope to remove them completely in the following months.</p><p>The adoption of TEA has made Nestful faster, more maintainable and easier to develop features for. Most importantly, however, those advantages are now the path of least resistance in Nestful&#8217;s development cycle.</p><p>I hope that this transition will continue to bear fruit as time passes. By reducing the need for testing, by encouraging better design and architecture, and most importantly &#8212; by being fun.</p><p>This post was mostly about the technical benefits of Gleam and Lustre, but there&#8217;s also an emotional one &#8212; programming in them is a much, much, much more enjoyable experience than the TS alternative.</p><p>As we put our best foot forward in an attempt to increase value for our customers we sometimes forget about ourselves, and if we can &#8212; we can and should enjoy the process. Programming can and should be fun.</p><p>Subscribe and stay tuned for more unique takes on personal productivity and novel(-ish) web technologies.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Spontaneous Productivity ! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[The myth of the perfect productivity system ]]></title><description><![CDATA[When organization becomes the biggest distraction of all]]></description><link>https://blog.nestful.app/p/myth-of-the-perfect-productivity</link><guid isPermaLink="false">https://blog.nestful.app/p/myth-of-the-perfect-productivity</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Mon, 17 Mar 2025 17:22:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!aZOI!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13336315-f7a5-451b-ae0a-2cf7a1f81850_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The problem is me. The system works. I just don&#8217;t have the discipline to apply it. That&#8217;s how I&#8217;ve started to feel about the <a href="https://johnnydecimal.com/">Johnny Decimal</a> system. It calls itself &#8220;a system to organize your life,&#8221; and, to a point, does &#8212; that point is right after I&#8217;ve spent hours organizing folders. It&#8217;s been a year since I applied it to my Obsidian vault and Gmail. For a few weeks, I&#8217;m on it: notes filed, emails slotted, everything is where it should be, and I feel pleased with myself. Superior, even.</p><p>Then reality sets in. In Obsidian, I conveniently forget that the system requires upkeep. I start adding notes haphazardly, leaving them unsorted, telling myself I&#8217;ll file them later. Fast-forward six months: I still haven&#8217;t filed them. Some &#8211; embarrassingly &#8211; don&#8217;t even have titles. The most success I&#8217;ve had with maintaining order is setting a recurring calendar reminder at the end of each month to clean up my mess. But even then, I find myself thinking: why don&#8217;t I just organize as I go?</p><p>Johnny Decimal works better in my Gmail, sort of. I have folders like &#8220;1 Work,&#8221; &#8220;2 Health,&#8221; and &#8220;3 Finance,&#8221; each with their own neatly nested subfolders. The problem folder is &#8220;0 Current,&#8221; my dumping ground. Everything lands in my inbox first. If I don&#8217;t want to deal with it immediately, I shove it in there. Spoiler: I don&#8217;t. &#8220;Current&#8221; is where tasks go to die.</p><p>Another issue: I can&#8217;t remember the decimal numbers. I don&#8217;t use the files often enough to internalize the structure. Sure, I recall that I have a &#8220;Current&#8221; folder with a &#8220;What&#8217;s on&#8221; subfolder, but I can never remember the numbers associated with them. And ultimately, I default to search anyway. I don&#8217;t navigate through &#8220;Car &gt; Insurance&#8221; to find my policy details, I type &#8220;car insurance&#8221; into the search bar.</p><p>This realization makes me wonder: am I organizing out of necessity, or because I&#8217;m hooked on the ritual? I like the Johnny Decimal system and the big-picture view it gives me. One organizing spree, and I&#8217;ve got finances, travel, random notes on lock. I haven&#8217;t mentioned work. Forget that. Coding a feature or juggling sprint tasks doesn&#8217;t bend to decimals, so it gets siloed off into a whole other system.</p><p>Some folks let their MacBook files rot and lean on Spotlight &#8212; because chaos apparently works for them. On a <a href="https://news.ycombinator.com/item?id=43128093">Hacker News thread</a> discussing the flaws of the Johnny Decimal system, one comment stood out:</p><blockquote><p>&#8220;As an ADHD person, I&#8217;ve found the best way for me is not to put effort into organizing at all&#8230; My brain works better searching than browsing.&#8221; </p></blockquote><p>This is what gets me &#8212; maybe organization isn&#8217;t meant to be organized.</p><p>But the real issue? Organizing is a form of avoidance. Not just avoidance of work, but avoidance of life itself. If I can just create the perfect list of movies to watch or the best places to buy furniture, then I&#8217;ll start living. Or then I&#8217;ll be safe because there will be just a little less chaos. Meanwhile, actual work (and living) sits there, not done.</p><p>Another Hacker News comment hit even harder:</p><blockquote><p>&#8220;The more I tried to control and organize my life, the more stressed I became. Digitizing and organizing my knowledge base, in particular, wasted countless hours. Recently, I decided to let go of that rigid structure and instead focus on naturally prioritizing the most important tasks for the day, week, and month. So far, this approach has been working well, or at least it feels like it is.&#8221;</p></blockquote><p>I went on a year-long journey organizing everything that might be useful someday. The only thing that&#8217;s actually been useful? Knowing where a scanned copy of my passport is. The next part of the Hacker News comment terrifies me because how could I possibly get things done without the illusion of organization? This is gonna take a fundamental mindset shift for me and anyone else who clings onto organization for dear life. Letting go of control feels insane, but what if that&#8217;s the point? What if the &#8220;messy desktop, just do it&#8221; crowd isn&#8217;t failing, but thriving?</p><p>The best productivity system could really be the one you don&#8217;t have to think about. Johnny Decimal&#8217;s solid if you babysit it, but the second you blink, it&#8217;s toast. I&#8217;m not a robot, and neither are you. My alternative system? Just go spontaneous and do what needs to be done. Keep what&#8217;s critical front and center. Nestful&#8217;s my wingman here: a to-do list that doesn&#8217;t care if my Obsidian is a mess or my inbox is full. Everything else can wait, because &#8212; shocker &#8212; it usually doesn&#8217;t matter. Productivity is not about control; it&#8217;s about action. Stop overthinking the how and just start. Trust me, you&#8217;ll survive the mess.</p><p>Try <a href="https://nestful.app/?utm_source=substack&amp;utm_medium=blog&amp;utm_campaign=myth">Nestful</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/subscribe?"><span>Subscribe now</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/p/myth-of-the-perfect-productivity?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/p/myth-of-the-perfect-productivity?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p>]]></content:encoded></item><item><title><![CDATA[Due dates are killing your productivity – here’s how to do the real work instead]]></title><description><![CDATA[A better system for steady progress]]></description><link>https://blog.nestful.app/p/due-dates-are-killing-your-productivity</link><guid isPermaLink="false">https://blog.nestful.app/p/due-dates-are-killing-your-productivity</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Mon, 10 Mar 2025 06:49:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Anc5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62976a71-df53-49f7-84eb-6921839598dd_1600x966.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Due dates are a procrastinator's best friend. I&#8217;d log them religiously into my calendar and to-do list. Yet, every time, I&#8217;d end up racing the clock, scrambling at the last minute to complete work. The pattern was consistent: tasks with far-off deadlines would sit untouched until urgency forced action. It wasn&#8217;t poor planning or lack of grit, it was me dodging the hard stuff. I&#8217;d break tasks down, sure, but I&#8217;d cherry-pick the easy bits and leave the real work for &#8220;later.&#8221; The fix? It&#8217;s not just making subtasks but timing them to force engagement, not avoidance.</p><h2>Falling into the trap of easy wins</h2><p>Devs, project managers, anyone with a pulse, we all know the drill: split a task into subtasks, pin a due date, call it done. So why do we still choke? Because we treat due dates like finish lines and subtasks like a buffet. A deadline two weeks out feels like a cushion, so we coast. Subtasks stack up, and we gravitate to the low-hanging fruit &#8211; tweaking a UI, writing a quick test &#8211; while the meaty stuff (say, wrestling with an API) festers. It&#8217;s not laziness; it&#8217;s human nature misreading the signals. <a href="https://en.wikipedia.org/wiki/Parkinson%27s_law">Parkinson&#8217;s Law</a> is more than just time bloating, it&#8217;s about us shrinking from the real work until the last second.</p><p>Take a developer with a feature due in two weeks. They know it needs backend logic, frontend integration, and testing. They might mentally map it out, but with the due date far off, they start with the fun part and by the time they dig into the hard stuff, blockers pop up, scope creeps, and they&#8217;re crunching late, cursing themselves. Sound familiar?</p><h2>Combatting avoidance with to-do dates</h2><p>Here&#8217;s the shift that changed everything: stop tracking when it&#8217;s due and start tracking when to work. Due dates create pressure; to-do dates create progress. You&#8217;re not just breaking tasks down, you&#8217;re assigning them moments that make you face the tough stuff head-on, not just the easy wins.</p><p>Imagine that same feature, due March 24. Instead of one looming deadline, you set:</p><ul><li><p>&#8220;Outline requirements and API specs&#8221; for March 8.</p></li><li><p>&#8220;Build backend logic&#8221; for March 13.</p></li><li><p>&#8220;Hook up the frontend&#8221; for March 16.</p></li><li><p>&#8220;Test and debug&#8221; for March 21.</p></li></ul><p>Now, you&#8217;re engaging early. The backend doesn&#8217;t get kicked down the road; it&#8217;s tee&#8217;d up when you&#8217;ve got bandwidth. Each to-do date is a nudge to tackle the real work, not just the easy bits you like doing.</p><h2>Some tasks resist splitting</h2><p>Not every job splits neatly and it shouldn&#8217;t. Refactoring legacy code, for instance, laughs at tidy subtasks. You can&#8217;t always say &#8220;fix auth today, optimize queries tomorrow&#8221; &#8211; it&#8217;s a messy, iterative beast. But that&#8217;s where to-do dates shine brighter. Instead of forcing a breakdown, set time to dive in: &#8220;Poke at the codebase for 45 minutes on Monday,&#8221; &#8220;Tweak auth logic on Wednesday,&#8221; &#8220;Chase down query bottlenecks on Friday.&#8221; Rather than being rigid steps, these are simple commitments to show up and have consistent contact with the problem in hand.</p><h2>How I make it work in practice</h2><p>Here&#8217;s my setup:</p><ul><li><p>Hard due dates (like &#8220;Deploy feature, March 6&#8221;) go in Google Calendar &#8211; non-negotiable stakes in the ground.</p></li><li><p>Subtasks and to-do dates live in <a href="https://nestful.app/?utm_source=substack&amp;utm_medium=blog&amp;utm_campaign=juggle">Nestful</a>, a dynamic to-do list that surfaces subtasks when they matter.</p></li></ul><p>I put &#8220;Deploy feature&#8221; as my parent task in Nestful, then the four tasks I mentioned above as subtasks, with their respective to-do dates. I then drop in the less neat tasks, like tackling legacy code, on specific days with an allocated amount of time I intend to spend on them. Example: Spend 45 minutes untangling queries on Friday. I don&#8217;t have to finish that task on Friday, but I&#8217;ve just gotta engage with it. This tactic allows engagement to snowball, the due date rolls around and things are in an almost-done kind of state. No sweating.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Anc5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62976a71-df53-49f7-84eb-6921839598dd_1600x966.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Anc5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62976a71-df53-49f7-84eb-6921839598dd_1600x966.png 424w, https://substackcdn.com/image/fetch/$s_!Anc5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62976a71-df53-49f7-84eb-6921839598dd_1600x966.png 848w, https://substackcdn.com/image/fetch/$s_!Anc5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62976a71-df53-49f7-84eb-6921839598dd_1600x966.png 1272w, https://substackcdn.com/image/fetch/$s_!Anc5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62976a71-df53-49f7-84eb-6921839598dd_1600x966.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Anc5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62976a71-df53-49f7-84eb-6921839598dd_1600x966.png" width="1456" height="879" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/62976a71-df53-49f7-84eb-6921839598dd_1600x966.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:879,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Anc5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62976a71-df53-49f7-84eb-6921839598dd_1600x966.png 424w, https://substackcdn.com/image/fetch/$s_!Anc5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62976a71-df53-49f7-84eb-6921839598dd_1600x966.png 848w, https://substackcdn.com/image/fetch/$s_!Anc5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62976a71-df53-49f7-84eb-6921839598dd_1600x966.png 1272w, https://substackcdn.com/image/fetch/$s_!Anc5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62976a71-df53-49f7-84eb-6921839598dd_1600x966.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For gnarly projects, I lean on micro-commitments to break the ice: &#8220;Open the editor and sketch one function&#8221; or &#8220;Run the old code and spot one flaw.&#8221; These tiny tasks (also complete with to-do dates) trick me into starting, and once I&#8217;m in, the real work flows.</p><p>Nestful&#8217;s Agenda View ties it together, showing me what&#8217;s ripe today and what can wait. It means there&#8217;s no guesswork, no cherry-picking.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hCeN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F557e0e13-00b9-48ce-8dac-d06827d1c0ca_1600x960.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hCeN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F557e0e13-00b9-48ce-8dac-d06827d1c0ca_1600x960.png 424w, https://substackcdn.com/image/fetch/$s_!hCeN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F557e0e13-00b9-48ce-8dac-d06827d1c0ca_1600x960.png 848w, https://substackcdn.com/image/fetch/$s_!hCeN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F557e0e13-00b9-48ce-8dac-d06827d1c0ca_1600x960.png 1272w, https://substackcdn.com/image/fetch/$s_!hCeN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F557e0e13-00b9-48ce-8dac-d06827d1c0ca_1600x960.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hCeN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F557e0e13-00b9-48ce-8dac-d06827d1c0ca_1600x960.png" width="1456" height="874" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/557e0e13-00b9-48ce-8dac-d06827d1c0ca_1600x960.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:874,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hCeN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F557e0e13-00b9-48ce-8dac-d06827d1c0ca_1600x960.png 424w, https://substackcdn.com/image/fetch/$s_!hCeN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F557e0e13-00b9-48ce-8dac-d06827d1c0ca_1600x960.png 848w, https://substackcdn.com/image/fetch/$s_!hCeN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F557e0e13-00b9-48ce-8dac-d06827d1c0ca_1600x960.png 1272w, https://substackcdn.com/image/fetch/$s_!hCeN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F557e0e13-00b9-48ce-8dac-d06827d1c0ca_1600x960.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Final thoughts</h2><p>Due dates and division into sub tasks lets you hide from the hard stuff, picking easy tasks to feel productive while the clock ticks. To-do dates flip that, they&#8217;re guideposts that make you face the work that matters, when it matters. Whether it&#8217;s a feature you can slice up or a refactor that defies division, the principle holds: schedule the effort, not just the outcome.</p><p>Try <a href="https://nestful.app/?utm_source=substack&amp;utm_medium=blog&amp;utm_campaign=juggle">Nestful</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/subscribe?"><span>Subscribe now</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/p/due-dates-are-killing-your-productivity?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/p/due-dates-are-killing-your-productivity?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[How I juggle wildly different freelance projects and don’t lose my mind]]></title><description><![CDATA[My hybrid system for staying on top of things]]></description><link>https://blog.nestful.app/p/how-i-juggle-wildly-different-freelance</link><guid isPermaLink="false">https://blog.nestful.app/p/how-i-juggle-wildly-different-freelance</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Mon, 03 Mar 2025 08:19:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ACyN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Freelancing is a game of self-imposed chaos. At first, things were simple, one big client at a time. But after sending out multiple pitches, I hit the jackpot: three new clients, all at once. The catch was that I had to work on all their projects simultaneously. What followed was a total collapse of structure.</p><p>I&#8217;d wake up at 6AM already behind, frantically bouncing between emails and trying to decipher my own half-written notes. One client needed revisions on a strategy doc, another was waiting on a pitch, and somewhere in between, I had to make sense of a last-minute brief I had completely forgotten about.</p><p>My immediate solution was task blocking in my Google Cal, but it quickly started looking like a losing game of Tetris, as tasks overran, interruptions took place and quickly everything became a big mess of overscheduling and subsequent rescheduling.</p><p>I started questioning myself: <em>How are other people managing this? We all have the same 24 hours in a day &#8212; isn&#8217;t that what Kim K said? Maybe I just don&#8217;t wanna work</em>. Had I taken on too much, or was I just terrible at time management?</p><h2>The reality check</h2><p>I know from experience that whenever you want to change something in your life, the first step is to be brutally honest with yourself. You need to ask: <em>How much do I really spend each month? How much do I actually work out?</em> Only then can you figure out why you&#8217;re not saving any money or seeing the fitness results you want.</p><p>It hadn&#8217;t occurred to me to apply this same level of honesty to my time. But once I did, the results were a real slap in the face. The amount of hours I <em>thought</em> I was working versus the actual time spent on deep work was actually embarrassing. Hours spent &#8220;working&#8221; were padded out with answering quick messages, checking X every 10 minutes and completing small insignificant tasks. Yes, I am distracted and avoidant.</p><p>I cleared everything from my Google Cal and stared hard at the blank Week View. The hours were there and I needed to understand how so many of them were going unaccounted for, if I were ever going to succeed in working for more than one client. I started to wonder if this was more about managing my own delusion about time than time itself.</p><h2>Tracking my time and restructuring my days</h2><p>To figure out where my time was going, I started backtracking everything in my calendar. I tracked every time I sat down to work, went to the kitchen, took a shower, and left the house for a few hours. The gaps were eye-opening &#8212; I spent more time on everything else than work itself. I told you, avoidant.</p><p>I read Cal Newport&#8217;s <em>Deep Work</em>, which helped me understand that I was filling so much of my time with shallow tasks and not getting to the important &#8220;deep&#8221; stuff. Day after day, I was filling my mornings with busywork, putting off deep work until the afternoon and then not having sufficient energy to do it.</p><p>I took some time to <s>wallow</s> reflect on what was happening and tapped into something I already knew about myself. I have the highest level of concentration in the early morning, so if I want to get anything meaningful done, I have to capitalize on those morning hours. The rest of the day, I could give away to shallow (but necessary) tasks.</p><p>The perk of freelancing of course is that you can plan around what works best for you. For me:</p><ul><li><p>Early mornings &#8594; Deep work (writing, strategy)</p></li><li><p>Afternoons &#8594; Study, admin, light tasks</p></li><li><p>Evenings &#8594; Offline time, no screens</p></li></ul><h2>Getting organized</h2><p>From my experience, any vague sense of organization starts with a list. I love lists &#8212; shopping lists, movies to watch, books to read, to-do lists. They work great on paper for some things and better on a computer, where you can edit and remove items for others. So, whenever I&#8217;ve got multiple tasks on-the-go, a list is typically involved.</p><p>I&#8217;ve also spent a lot of time playing around with Kanban boards; there&#8217;s something satisfying about dragging tasks from To Do to Doing to Done. But when I&#8217;m managing multiple projects with shifting deadlines, I&#8217;m still having to put in the time to manually shuffle everything around. This led me to trying AI scheduling tools but I never went further than the free trial because there was too much manual setup to get it exactly how I wanted it &#8212; all time taken away from actually working.</p><p>Each time I started to feel overwhelmed by these tools, I&#8217;d default back to old-school list writing in Obsidian, then keying in deadlines and meetings to my Google Cal. I knew things could be more efficient but they work fine<em>-ish</em> like this.</p><p>So, it went like this for a while:</p><ol><li><p>Prepare my list in Obsidian the evening before.</p></li><li><p>Get up early, complete the most important &#8220;deep&#8221; work and not touch any of the &#8220;shallow&#8221; stuff or social media until the afternoon &#8211; which still to this day takes a lot of discipline.</p></li><li><p>Prepare a new list for the next day. This was a crucial step &#8212; spending time figuring out what I&#8217;d be doing the next morning &#8212; so I could dive straight into it.</p></li></ol><h2>My current hybrid system</h2><p>I&#8217;ve now created a system that works even better than this. I don&#8217;t need to spend time each evening figuring out what to do the next day anymore. I plan the upcoming week on Friday &#8212; and that&#8217;s it.</p><p>I&#8217;m still using Google Cal but I&#8217;m also using an app called Nestful. Google Cal is predominately where I put my fixed meetings and block out time where I know I will be sitting at my desk. I use Nestful alongside this &#8212; it&#8217;s basically a dynamic to-list, so it orders your tasks based on deadlines and tells you what you need to work on as soon as you sit down to work.</p><p>Here&#8217;s how it works: I add all the events that need to happen at specific times and places to my calendar. This often fills up further in advance than a week, but Friday is where I do my regular calendar check-up, so I know what&#8217;s coming up. Next week, I&#8217;ve got a couple of client check-ins, a few deadlines to keep an eye on and some on-site filming events &#8212; these all need to happen at specific times. The rest of the time, when I know I&#8217;ll be sitting at home at my desk, I block out with &#8220;Spontaneous work.&#8221; This is where I will go into Nestful and complete whatever it tells me is due next. <br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ACyN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ACyN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png 424w, https://substackcdn.com/image/fetch/$s_!ACyN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png 848w, https://substackcdn.com/image/fetch/$s_!ACyN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png 1272w, https://substackcdn.com/image/fetch/$s_!ACyN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ACyN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png" width="1456" height="1088" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1088,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ACyN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png 424w, https://substackcdn.com/image/fetch/$s_!ACyN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png 848w, https://substackcdn.com/image/fetch/$s_!ACyN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png 1272w, https://substackcdn.com/image/fetch/$s_!ACyN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd121be23-e180-4f3b-bd9b-a87266b04f69_1600x1196.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Instead of &#8220;Spontaneous work,&#8221; why not add the tasks to the calendar directly? Let&#8217;s revisit my Tetris point. Life is too unpredictable. A client requests something urgently, the dog pukes on the nice rug, mother calls for a chat &#8212; each of these derails the task-blocked calendar instantly. Maintaining the calendar becomes a task in itself and, as you&#8217;ll already have noted, I have too many tasks as it is.</p><p>Now, onto Nestful. I drop in all the tasks I need to do into its homepage. Each task can nest smaller tasks, so I give the main task the client&#8217;s name and then nest all the tasks required and due dates within it.</p><p>Here&#8217;s what it looks like within my client task:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0bf0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0fe86da-731f-4ce0-9cf9-4bc7c1038e81_1600x952.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0bf0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0fe86da-731f-4ce0-9cf9-4bc7c1038e81_1600x952.png 424w, https://substackcdn.com/image/fetch/$s_!0bf0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0fe86da-731f-4ce0-9cf9-4bc7c1038e81_1600x952.png 848w, https://substackcdn.com/image/fetch/$s_!0bf0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0fe86da-731f-4ce0-9cf9-4bc7c1038e81_1600x952.png 1272w, https://substackcdn.com/image/fetch/$s_!0bf0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0fe86da-731f-4ce0-9cf9-4bc7c1038e81_1600x952.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0bf0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0fe86da-731f-4ce0-9cf9-4bc7c1038e81_1600x952.png" width="1456" height="866" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c0fe86da-731f-4ce0-9cf9-4bc7c1038e81_1600x952.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:866,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0bf0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0fe86da-731f-4ce0-9cf9-4bc7c1038e81_1600x952.png 424w, https://substackcdn.com/image/fetch/$s_!0bf0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0fe86da-731f-4ce0-9cf9-4bc7c1038e81_1600x952.png 848w, https://substackcdn.com/image/fetch/$s_!0bf0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0fe86da-731f-4ce0-9cf9-4bc7c1038e81_1600x952.png 1272w, https://substackcdn.com/image/fetch/$s_!0bf0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0fe86da-731f-4ce0-9cf9-4bc7c1038e81_1600x952.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;ve done the same thing for my two other clients, adding in all of the tasks and due dates. Now, when I go to the Nestful homepage, it looks like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gNga!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf376239-be92-4c66-a5d9-ab3ae1f675be_1600x949.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gNga!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf376239-be92-4c66-a5d9-ab3ae1f675be_1600x949.png 424w, https://substackcdn.com/image/fetch/$s_!gNga!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf376239-be92-4c66-a5d9-ab3ae1f675be_1600x949.png 848w, https://substackcdn.com/image/fetch/$s_!gNga!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf376239-be92-4c66-a5d9-ab3ae1f675be_1600x949.png 1272w, https://substackcdn.com/image/fetch/$s_!gNga!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf376239-be92-4c66-a5d9-ab3ae1f675be_1600x949.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gNga!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf376239-be92-4c66-a5d9-ab3ae1f675be_1600x949.png" width="1456" height="864" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf376239-be92-4c66-a5d9-ab3ae1f675be_1600x949.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:864,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gNga!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf376239-be92-4c66-a5d9-ab3ae1f675be_1600x949.png 424w, https://substackcdn.com/image/fetch/$s_!gNga!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf376239-be92-4c66-a5d9-ab3ae1f675be_1600x949.png 848w, https://substackcdn.com/image/fetch/$s_!gNga!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf376239-be92-4c66-a5d9-ab3ae1f675be_1600x949.png 1272w, https://substackcdn.com/image/fetch/$s_!gNga!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf376239-be92-4c66-a5d9-ab3ae1f675be_1600x949.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I can see exactly what is due today and get a holistic view of all my tasks coming up. Once today&#8217;s tasks are completed and checked off, they get archived. Tomorrow when I open Nestful, the Tomorrow column will have shifted to Today.</p><p>Nestful shifts the focus away from time allocation and scheduling to getting things done. All I need to know is that I have a two-hour window of concentrated work scheduled in my calendar, and then Nestful does the work of telling me exactly what to focus on next. There&#8217;s no need to schedule and reschedule tasks because they&#8217;re just there, waiting to be completed.</p><p>If you&#8217;re a freelancer struggling to keep up, I recommend trying this. The combination of structured flexibility is the only thing that&#8217;s actually worked for me so far.</p><p>This system keeps me sane. No decision fatigue. No wasted minutes debating priorities. Just work getting done.</p><p><a href="https://nestful.app/?utm_source=substack&amp;utm_medium=blog&amp;utm_campaign=juggle">https://nestful.app/</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/subscribe?"><span>Subscribe now</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/p/how-i-juggle-wildly-different-freelance?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/p/how-i-juggle-wildly-different-freelance?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Gleam's Lustre is Frontend Development's Endgame]]></title><description><![CDATA[How a combination of language, architecture and ecosystem lead to more maintainable code and a more enjoyable experience.]]></description><link>https://blog.nestful.app/p/gleams-lustre-is-frontend-developments</link><guid isPermaLink="false">https://blog.nestful.app/p/gleams-lustre-is-frontend-developments</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Wed, 20 Nov 2024 13:06:58 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/393eaf80-4223-4d50-9eaf-48a81c1f336a_1456x1048.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a launch post for a tool I built called OmniMessage, which aspires to be the final nail in coffin of client-server state synchronizing problems.</p><p>OmniMessage is written in <a href="https://gleam.run">Gleam</a>, a language that compiles to both Erlang and JavaScript, and is an extension of  <a href="https://hexdocs.pm/lustre/">Lustre</a>, Gleam&#8217;s major frontend framework.</p><p>To understand the benefits of OmniMessage and how it works, we first need we&#8217;ll first need explore how Gleam and Lustre provide one of the best developer experiences you can find for the web these days, and how building on top of them can improve that experience even more.</p><p>If you&#8217;re already familiar with The Elm Architecture and functional languages, you can <a href="https://blog.nestful.app/i/151830772/introducing-omnimessage">click here to skip to the OmniMessage part</a>, however going through the Lustre tutorial really puts the problem it solves in perspective.</p><h2>A Functional C</h2><p>Gleam&#8217;s website says:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!V5zS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e2c2dcd-7423-4666-ad39-120c4893efa6_927x407.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!V5zS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e2c2dcd-7423-4666-ad39-120c4893efa6_927x407.png 424w, https://substackcdn.com/image/fetch/$s_!V5zS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e2c2dcd-7423-4666-ad39-120c4893efa6_927x407.png 848w, https://substackcdn.com/image/fetch/$s_!V5zS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e2c2dcd-7423-4666-ad39-120c4893efa6_927x407.png 1272w, https://substackcdn.com/image/fetch/$s_!V5zS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e2c2dcd-7423-4666-ad39-120c4893efa6_927x407.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!V5zS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e2c2dcd-7423-4666-ad39-120c4893efa6_927x407.png" width="927" height="407" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8e2c2dcd-7423-4666-ad39-120c4893efa6_927x407.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:407,&quot;width&quot;:927,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:50398,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!V5zS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e2c2dcd-7423-4666-ad39-120c4893efa6_927x407.png 424w, https://substackcdn.com/image/fetch/$s_!V5zS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e2c2dcd-7423-4666-ad39-120c4893efa6_927x407.png 848w, https://substackcdn.com/image/fetch/$s_!V5zS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e2c2dcd-7423-4666-ad39-120c4893efa6_927x407.png 1272w, https://substackcdn.com/image/fetch/$s_!V5zS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8e2c2dcd-7423-4666-ad39-120c4893efa6_927x407.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Gleam powers the app this blog is for, <a href="https://nestful.app">Nestful</a>. Which I am <a href="https://blog.nestful.app/p/why-i-rewrote-nestful-in-gleam">incrementally rewriting</a>.</p><p>That other blog post already covers a lot of Gleam&#8217;s advantages and the specific considerations and tradeoffs as they pertain to Nestful. Those still apply, but when we look at frontend development in general, one key trait of Gleam is crucial: </p><p><strong>Gleam is a functional language with a C-style syntax.</strong></p><p>That is an indispensable advantage on the journey to ecosystem nirvana.</p><p>Here&#8217;s some for you to bask in. It is a very pleasant language:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://tour.gleam.run/flow-control/list-patterns/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CG1J!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f7f5c0d-b035-4a3f-8de0-123a435be818_1260x939.png 424w, https://substackcdn.com/image/fetch/$s_!CG1J!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f7f5c0d-b035-4a3f-8de0-123a435be818_1260x939.png 848w, https://substackcdn.com/image/fetch/$s_!CG1J!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f7f5c0d-b035-4a3f-8de0-123a435be818_1260x939.png 1272w, https://substackcdn.com/image/fetch/$s_!CG1J!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f7f5c0d-b035-4a3f-8de0-123a435be818_1260x939.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CG1J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f7f5c0d-b035-4a3f-8de0-123a435be818_1260x939.png" width="1260" height="939" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8f7f5c0d-b035-4a3f-8de0-123a435be818_1260x939.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:939,&quot;width&quot;:1260,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:119673,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://tour.gleam.run/flow-control/list-patterns/&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CG1J!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f7f5c0d-b035-4a3f-8de0-123a435be818_1260x939.png 424w, https://substackcdn.com/image/fetch/$s_!CG1J!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f7f5c0d-b035-4a3f-8de0-123a435be818_1260x939.png 848w, https://substackcdn.com/image/fetch/$s_!CG1J!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f7f5c0d-b035-4a3f-8de0-123a435be818_1260x939.png 1272w, https://substackcdn.com/image/fetch/$s_!CG1J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f7f5c0d-b035-4a3f-8de0-123a435be818_1260x939.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Straight from Gleam&#8217;s tour! Click to jump to the code</figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://tour.gleam.run/functions/pipelines/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Dyaz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F540e386b-b59c-4f2e-b94b-b8b41e7de5be_1255x897.png 424w, https://substackcdn.com/image/fetch/$s_!Dyaz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F540e386b-b59c-4f2e-b94b-b8b41e7de5be_1255x897.png 848w, https://substackcdn.com/image/fetch/$s_!Dyaz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F540e386b-b59c-4f2e-b94b-b8b41e7de5be_1255x897.png 1272w, https://substackcdn.com/image/fetch/$s_!Dyaz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F540e386b-b59c-4f2e-b94b-b8b41e7de5be_1255x897.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Dyaz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F540e386b-b59c-4f2e-b94b-b8b41e7de5be_1255x897.png" width="1255" height="897" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/540e386b-b59c-4f2e-b94b-b8b41e7de5be_1255x897.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:897,&quot;width&quot;:1255,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:119842,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://tour.gleam.run/functions/pipelines/&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Dyaz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F540e386b-b59c-4f2e-b94b-b8b41e7de5be_1255x897.png 424w, https://substackcdn.com/image/fetch/$s_!Dyaz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F540e386b-b59c-4f2e-b94b-b8b41e7de5be_1255x897.png 848w, https://substackcdn.com/image/fetch/$s_!Dyaz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F540e386b-b59c-4f2e-b94b-b8b41e7de5be_1255x897.png 1272w, https://substackcdn.com/image/fetch/$s_!Dyaz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F540e386b-b59c-4f2e-b94b-b8b41e7de5be_1255x897.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Here&#8217;s another. Check out those pipes</figcaption></figure></div><p>We know that frontend developers like functional paradigms. TypeScript functional-like libraries continue to pop up,  some developers really like React, and even when they dislike those, they still argue about it <a href="https://github.com/airbnb/javascript/issues/1271">on AirBnB&#8217;s style guide repository</a>.</p><p>Even with all the that functional engagement in mind, however you slice it, frontend development is all in JavaScript-land, and JavaScript has a C-style syntax.</p><p>In my opinion, this is a significant part of why previous attempts like Elm, Reason/ReScript and PureScript did not reach the success they should have. It is also why Flutter made such huge strides with frontend developers. Flutter&#8217;s developer experience is excellent, and Dart sure does feel a lot like TypeScript.</p><p>The fact that Gleam has that kind of syntax will help the most crucial part of going mainstream &#8212; ecosystem growth. It&#8217;s going to be nice to have a functional language that&#8217;s not only simple and type safe, but also has a large ecosystem.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/p/gleams-lustre-is-frontend-developments?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Nestful&#8217;s Substack! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/p/gleams-lustre-is-frontend-developments?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/p/gleams-lustre-is-frontend-developments?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><h2>Enter Lustre</h2><p>If I had to describe Lustre with only a few words, I would say it is Gleam&#8217;s Elm and LiveView.</p><p>Yes, Elm <strong>and</strong> LiveView. While not a 1-to-1 match, Lustre is a Model-View-Update framework that can run both as a single-page application, like Elm, on the server like LiveView, and with OmniMessage &#8212; well, you&#8217;ll see.</p><p>I am not going to define Model-View-Update. Instead, I&#8217;m going to teach you some Lustre. By the time we&#8217;re done you&#8217;ll know exactly what it means.</p><p>We&#8217;ll start with a counter example, which is small and contained, then continue to build a (very, very minimal) chat app. Follow along with the code, it&#8217;s nice seeing how the different pieces fall into place.</p><p>First, let&#8217;s keep our data in a model, and have a function initializing it:</p><pre><code>type Model {
  Model(count: Int)
}

fn init(initial_count: Int) -&gt; Model {
  Model(int.max(0, initial_count))
}</code></pre><p>A <code>Model</code> type that contains a single integer, which we initialize to a given value, but not lower than zero. Fairly simple.</p><p>Next, we&#8217;ll define messages that can operate on that model:</p><pre><code>import gleam/int

import lustre/effect.{type Effect}

type Msg {
  Increment
  Decrement
}

fn update(model: Model, msg: Msg) -&gt; #(Model, Effect(Msg)) {
  case msg {
    Increment -&gt; #(Model(model.count + 1), effect.none())
    Decrement -&gt; #(Model(int.max(0, model.count - 1)), effect.none())
  }
}</code></pre><p>Our update function returns a new count based on the message it receives, adding or substracting from the original. Don&#8217;t mind the <code>effect.none()</code> part for now &#8212; we&#8217;ll get to that later.</p><p>Finally, we&#8217;ll have a function for displaying a user interface. It&#8217;s important to note that all the view functions in this post use <code>lustre_pipes</code>, which is an extension of Lustre&#8217;s view utilities that I consider easier to read:</p><pre><code>import lustre_pipes/attribute
import lustre_pipes/element.{type Element}
import lustre_pipes/element/html
import lustre_pipes/event

fn view(model: Model) -&gt; Element(Msg) {
  let count = int.to_string(model.count)

  html.div()
  |&gt; attribute.class("h-full w-full flex justify-center items-center")
  |&gt; element.children([
    html.button()
      |&gt; event.on_click(Decrement)
      |&gt; element.text_content("-"),
    html.p()
      |&gt; element.text_content(count),
    html.button()
      |&gt; event.on_click(Increment)
      |&gt; element.text_content("+"),
  ])
}</code></pre><p>When we hand the <code>init</code>, <code>update</code>, and <code>view</code> functions to Lustre starts a runtime that:</p><ol><li><p> Calls <code>init</code> with an initial value</p></li><li><p> Uses the resulting model to render a view</p></li><li><p> Listens to events and calls <code>update</code> with any dispatched messages, then back to #2</p></li></ol><p>Model, view, update.</p><p>Because views are pure functions, meaning they do not perform any side-effects, they can be used anywhere. Every time we&#8217;ll hand the same model into that function, we&#8217;re going to get the same view. Every. Single Time.</p><p>This makes features like hydration very simple. Since this is a deterministic state machine, all we need is the current state. In Lustre, hydration means simply sending the model alongside the rendered HTML. Lustre will use that model to create a view of its own and compare it to the prerendered HTML. If they match, the app is &#8220;hydrated&#8221;.</p><p>But I digress. We&#8217;re here to talk about client-server state management, so let&#8217;s continue, building a small chat app.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Nestful&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>A LiveView Single Page Application</h2><p>Our app should be able to:</p><ol><li><p>Send chat messages</p></li><li><p>Display chat messages and their status (sending, sent, received, etc)</p></li><li><p>Have a button that scrolls down to the latest chat message</p></li><li><p>Show the amount of active users in the chat room</p></li></ol><p>We&#8217;re missing some key features such as receiving messages, chat room creation and user authentication. It doesn&#8217;t matter. Even this confined use case of a single room with a single user will show us the benefits of managing state with Lustre.</p><p>We&#8217;re going to build a single page application first, then use the fundamentals for the server as well. The conversion will be trivial.</p><p>First, our model:</p><pre><code>// For date handling
import birl

pub type Model {
  Model(
    messages: List(ChatMessage),
    draft_content: String,
  )
}

pub type ChatMessage {
  ChatMessage(
    id: String,
    content: String,
    status: ChatMessageStatus,
    sent_at: birl.Time,
  )
}

pub type ChatMessageStatus {
  ClientError
  ServerError
  Sent
  Received
  Sending
}</code></pre><p>Note that to avoid confusing chat messages and Lustre messages, the former will be strictly called a chat message.</p><p>We&#8217;ll initialize our model with an empty draft and empty chat messages.</p><pre><code>fn init(_) -&gt; #(Model, effect.Effect(Msg)) {
  #(Model([], draft_content: ""), effect.none())
}</code></pre><p>Again, don&#8217;t worry about that <code>effect.none()</code> for now. </p><p>To define our <code>Msg</code> type, first let&#8217;s think through what we need to do. Lustre suggests using a SubjectVerbObject (SVO) structure:</p><ol><li><p><code>UserUpdateDraftContent</code></p></li><li><p><code>UserSendChatMessage</code></p></li><li><p><code>UserScrollToLatest</code></p></li><li><p><code>ServerSentChatMessages</code></p></li></ol><p>Number 4 is for getting back messages we sent to the server, not for other user&#8217;s messages, which is out of scope. This is more of a bring-your-own-refresh kind of app.</p><p>Having those messages written in SVO not only makes it easy to reason about how the app works, but also makes it easier to debug later, if and when needed.</p><p>Here is an update function that takes care of all the simple bits:</p><pre><code>// For uuid generation
import gluid


pub type Msg {
  UserUpdateDraftContent(String)
  UserSendChatMessage
  UserScrollToLatest
  ServerSentChatMessages(List(ChatMessage))
}

fn update(model: Model, msg: Msg) -&gt; #(Model, Effect(Msg)) {
  case msg {
<strong>    </strong>UserUpdateDraftContent(draft_content) -&gt; #(
      Model(..model, draft_content:),
      effect.none(),
    )
    UserSendChatMessage -&gt; {
      let chat_msg =
        Message(
          id: gluid.guidv4() |&gt; string.lowercase(),
          content: model.draft_message,
          status: shared.Sending,
          sent_at: birl.utc_now(),
        )

      let chat_msgs = [chat_msg, ..model.chat_msgs]

      #(Model(..model, chat_msgs:), effect.none())
    }
  }
}</code></pre><p>How lovely is that <code>case</code>, which the Gleam compiler should mark as an error. Gleam makes sure <code>case</code> is exhaustive &#8212; it has our back in guaranteeing we address all of the different messages that the <code>update</code> function should be able to handle.</p><p>But wait! There&#8217;s a design problem. We need to clean the draft after sending it, but that doesn&#8217;t make sense to do in the <code>UserSendChatMessage</code> branch &#8212; chat messages can come from other places. A &#8220;scheduled messages&#8221; queue, for example. We need another message, <code>UserSendDraft</code>:</p><pre><code>pub type Msg {
  UserSendDraft
  UserUpdateDraftContent(String)
  UserSendChatMessage(String)
  UserScrollToLatest
  ServerSentChatMessages(List(ChatMessage))
}

fn update(model: Model, msg: Msg) -&gt; #(Model, Effect(Msg)) {
  case msg {
    UserUpdateDraftContent(content) -&gt; #(
      Model(..model, draft_content: content),
      effect.none(),
    )
    UserSendChatMessage(chat_msg) -&gt; {
      let chat_msgs = [chat_msg, ..model.chat_msgs]

      #(Model(..model, chat_msgs:), effect.none())
    }
    UserSendDraft -&gt; #(
      Model(..model, draft_content: ""),
      effect.from(fn(dispatch) {
        Message(
          id: gluid.guidv4() |&gt; string.lowercase(),
          content: model.draft_content,
          status: shared.Sending,
          sent_at: birl.utc_now(),
        )
        |&gt; UserSendChatMessage
        |&gt; dispatch
      }),
    )
  }
}</code></pre><p><code>UserSendDraft</code> returns not only the model, but also something called an <code>Effect</code>.</p><p>An effect is a type that instructs Lustre&#8217;s runtime to perform tasks on our behalf. In this case we ask Lustre to dispatch <code>UserSendChatMessage</code> with the message, by returning that effect alongside our model. <code>effect.none()</code> simply tells Lustre that there&#8217;s nothing to do, which is the case for the rest of our <code>case</code> branches.</p><p>Side effects are a must in most apps, especially on the web. To be able to use them while keeping our update function pure (meaning, having the same output every time it receives the same input), MVU delegates their execution to the runtime. If we want a side effect to happen, we have to ask the runtime to perform it.</p><p>Now that we know effects exist, we&#8217;ll use them to implement <code>UserScrollToLatest</code>. </p><p>For that we need need a container to scroll. Enter, a view function:</p><pre><code>fn status_string(status: ChatMessageStatus) {
  case status {
    ClientError -&gt; "Client Error"
    ServerError -&gt; "Server Error"
    Sent -&gt; "Sent"
    Received -&gt; "Received"
    Sending -&gt; "Sending"
  }
}

fn chat_message_element(chat_msg: ChatMessage) {
  html.div()
  |&gt; element.children([
    html.p()
    |&gt; element.text_content(
      status_string(chat_msg.status) &lt;&gt; ": " &lt;&gt; chat_msg.content,
    ),
  ])
}

fn sort_chat_messages(chat_msgs: List(ChatMessage)) {
  use a, b &lt;- list.sort(chat_msgs)
  birl.compare(a.sent_at, b.sent_at)
}

fn view(model: Model) -&gt; element.Element(Msg) {
  let sorted_chat_msgs =
    model.chat_msgs
    |&gt; sort_chat_messages

  html.div()
  |&gt; attribute.class(
    "h-full flex flex-col justify-center items-center gap-y-5"
  )
  |&gt; element.children([
    html.div()
      |&gt; attribute.id("chat-msgs")
      |&gt; attribute.class(
      "h-80 w-80 overflow-y-auto p-5 border border-gray-400 rounded-xl",
      )
      |&gt; element.keyed({
        use chat_msg &lt;- list.map(sorted_chat_msgs)
        #(chat_msg.id, chat_message_element(chat_msg))
      }),
    html.form()
      |&gt; attribute.class("w-80 flex gap-x-4")
      |&gt; event.on_submit(UserSendDraft)
      |&gt; element.children([
        html.input()
          |&gt; event.on_input(UserUpdateDraftContent)
          |&gt; attribute.type_("text")
          |&gt; attribute.value(model.draft_content)
          |&gt; attribute.class(
            "flex-1 border border-gray-400 rounded-lg p-1.5")
          |&gt; element.empty(),
        html.input()
          |&gt; attribute.type_("submit")
          |&gt; attribute.value("Send")
          |&gt; attribute.class(
      "border border-gray-400 rounded-lg p-1.5 text-gray-700 font-bold",
          )
          |&gt; element.empty(),
      ]),
  ])
}
</code></pre><p>Note how event handlers must return a <code>Msg</code> for our update function to handle. </p><p><code>event_onsubmit</code> accepts a straight up <code>Msg</code>, so we just put <code>UserSendDraft</code>.</p><p><code>on_input</code> accepts a function of the type <code>fn(String) &#8594; Msg</code>, which is exactly what <code>UserUpdateDraftContent</code> is. The following is identical:</p><pre><code>|&gt; event.on_input(fn(value: String) {
  UserUpdateDraftContent(value)
})</code></pre><p>If you know HTML, the rest is fairly straightforward except for two parts: <code>use</code> and <code>element.keyed</code>.</p><p><code>use</code> is syntactic sugar for a final-argument callback. Everything before the arrow is the callback&#8217;s arguments, lines below the <code>use</code> are the callback&#8217;s body.</p><p>This means these two are equivalent:</p><pre><code>fn sort_chat_messages(chat_msgs: List(ChatMessage)) {
  use a, b &lt;- list.sort(chat_msgs)
  birl.compare(a.sent_at, b.sent_at)
}

fn sort_chat_messages(chat_msgs: List(ChatMessage)) {
  chat_msgs
  |&gt; list.sort(fn(a, b) {
    birl.compare(a.sent_at, b.sent_at)
  })
}</code></pre><p>You know a language is simple when <code>use</code> is its most &#8220;complicated&#8221; part.</p><p><code>element.keyed</code> creates an element whose children have a unique identifier (the key) attached to them such that when Lustre has to re-render the list itself, it knows which elements changed and which didn&#8217;t. This is similar to React&#8217;s or Vue&#8217;s <code>key</code> property and is done in Lustre by giving the <code>element.keyed()</code> function a list of tuples in the form of <code>#(key, Element)</code>.</p><p>Now that we have our view, we can handle <code>UserScrollToLatest</code>:</p><pre><code># for handling possible errors
import gleam/result
# for interacting with the DOM
import plinth/browser/element as plinth_element

fn update(model: Model, msg: Msg) -&gt; #(Model, Effect(Msg)) {
  case msg {
    // other handlers omitted for brevity
    UserScrollToLatest -&gt; #(model, scroll_to_latest_message())
  }
}

const msgs_container_id = "chat-msgs"

fn scroll_to_latest_message() {
  effect.from(fn(_dispatch) {
    let _ =
      document.get_element_by_id(msgs_container_id)
      |&gt; result.then(fn(container) {
        plinth_element.scroll_height(container)
        |&gt; plinth_element.set_scroll_top(container, _)
        Ok(Nil)
      })

    Nil
  })
}</code></pre><p>This code uses <code>plinth</code>, an library to interact with browser APIs, to first find the container by its id (<code>get_element_by_id</code>), and if found (<code>result.then</code>), scroll.</p><p>By extracting this effect to a separate function, we can include it in other places, like automatically scrolling on <code>UserSendChatMessage</code>.</p><p>By now you should experience how Gleam and Lustre make for this very structured, harmonious development experience, where everything has a place in the render loop.</p><p>Now that we have the client side taken care of, let&#8217;s address the server. As you&#8217;ve probably guessed, talking to the server is a side effect, meaning we&#8217;ll instruct Lustre to make the talking on our behalf. Luckily there is a package that does just that:</p><pre><code>import lustre_http as http

fn update(model: Model, msg: Msg) -&gt; #(Model, Effect(Msg)) {
  case msg {
    // other handlers omitted for brevity
    ClientMessage(shared.UserSendChatMessage(chat_msg)) -&gt; {
      let chat_msgs = [chat_msg, ..model.chat_msgs]

      #(
        Model(..model, chat_msgs:),
        effect.batch([
          scroll_to_latest_message(),
          http.post(
            "/chat-message",
            [chat_msg] |&gt; chat_msgs_to_json,
            http.expect_json(
              chat_msgs_from_json,
              ServerSentChatMessages,
            ),
          ),
        ]
      )
    }
  }
}</code></pre><p>This takes care of creating the message on the server. We still update the model with the new chat message in <code>Sending</code> state, and we continue with <code>effect.batch</code>.</p><p><code>effect.batch</code> takes several effects and combines them to a single one for our <code>update</code> function. The first one is our scroll effect that&#8217;ll happen after sending a message. The second will HTTP POST that message to the path <code>/chat_message</code>.</p><p>That effect is from the <code>lustre_http</code> library (that we import as <code>http</code>), that can create effects of HTTP requests. The arguments for creating the POST effect are:</p><ol><li><p>The path to POST to, in our case <code>/chat-message</code></p></li><li><p>The body of the post request, in our case a <code>JSON.stringify</code>-ed chat message</p></li><li><p>A description of the result, (JSON) and its handler (<code>ServerSentChatMessages</code>).</p></li></ol><p>In a production chat app it would have been better to use websockets. The websocket effect works very similarly but requires more setup due to the nature of websockets. Since boilerplate does not add to our learning, we demonstrate using regular HTTP.</p><p>When Lustre will execute this effect, the following will happen:</p><ol><li><p>It will post our encoded chat message to <code>/chat-message</code></p></li><li><p>It will try parsing it as JSON using <code>chat_msgs_from_json</code></p></li><li><p>On success, it will dispatch <code>ServerSendChatMessages(Ok(messages))</code></p></li><li><p>On error, it will dispatch <code>ServerSendChatMessages(Error(error))</code></p></li></ol><p>Gleam, you see, has errors as values. That means that except for problematic FFI, functions never throw. You must deal with errors as they come or consciously defer their handling. This pairs fantastically with <code>case</code>&#8217;s exhaustiveness checks:</p><pre><code>pub type Msg {
  // accepts a `Result` from `lustre_http`'s effect:
  ServerSentChatMessages(Result(List(ChatMessage), http.HttpError))
}

fn update(model: Model, msg: Msg) -&gt; #(Model, Effect(Msg)) {
  case msg {
    // Gleam will make sure both variants are present. Lovely.
    ServerSentChatMessage(Ok(List(ChatMessage)) -&gt; todo
    ServerSentChatMessage(Error(error)) -&gt; todo
  }
}</code></pre><p>Again, we have a design problem. The server is the source of truth for chat messages, so we&#8217;d like it to override our local copies (that&#8217;s how a <code>Sending</code> chat message will become a <code>Sent</code> chat message). However doing that for a list can be quite costly.</p><p>Let&#8217;s change our state to hold chat messages in a dictionary, instead:</p><pre><code>pub type Model {
  Model(
    messages: Dict(String, ChatMessage),
    draft_content: String,
  )
}</code></pre><p>And implement <code>ServerSendChatMessage</code>:</p><pre><code>fn update(model: Model, msg: Msg) -&gt; #(Model, Effect(Msg)) {
  case msg {
    ServerSentChatMessage(Ok(server_chat_msgs)) -&gt; {
      let chat_msgs =
        model.chat_msgs
        |&gt; dict.merge(server_chat_msgs)

      #(Model(..model, chat_msgs:), effect.none())
    }
    ServerSentChatMessage(Error(error)) -&gt; {
      // this is where you'd show, say, an error toast
      #(model, effect.none())
    }
    // changes to other branches available in the full code below
  }
}</code></pre><p>As with any project, the more time we spend writing its code, the more we learn about it and about the solutions it demands. With MVU, those changes are easy to adapt to since the state mechanism stays the same. By keeping the state handling mechanism completely separate from our project&#8217;s design choices, we avoid having to change it when we inevitably discover we made poor ones.</p><p>To complete our server-communication portion for chat messages, let&#8217;s fetch on <code>init</code>:</p><pre><code>fn init(_) -&gt; #(Model, effect.Effect(Msg)) {
  #(
    Model(dict.new(), draft_content: ""),
    http.get(
      "/chat-message",
      http.expect_json(
        chat_msgs_from_json,
        ServerSentChatMessages,
      ),
    ),
  )
}</code></pre><p>So far this should have been a relatively pleasant experience of writing a very regular app, with all the pros and cons that come with it. Our state handling approach forever puts us on alert, having to make sure our local copy of the chat is up to date with the server&#8217;s copy &#8212; the source of truth. This is a very common SPA issue.</p><p>I can hear the LiveView gang collectively yelling into the past as I&#8217;m typing. &#8220;Keep everything on the server&#8221;, they say. Well, we&#8217;re about to. &#8212; &#8220;and get rid of that REST mess!&#8221;. Ok, I heard you, we&#8217;re about to.</p><p>Our first step of evolution will be to implement our final missing feature:</p><blockquote><p>Show the amount of active users in the chat room</p></blockquote><p>I sneakily did not include a <code>Msg</code> for handling this when we built our <code>update</code> function. This information is strictly server-side, with no interaction, and most importantly &#8212; <strong>it is meaningless when we&#8217;re offline.</strong></p><p>It&#8217;s a no-brainer to run it exclusively on the server. This is where the LiveView part of &#8220;Elm <strong>and</strong> LiveView&#8221; comes in. We can take this Lustre component:</p><pre><code>type Model {
  Model(user_count: Option(Int))
}

fn init(count_listener: fn(fn(Int) -&gt; Nil) -&gt; Nil) {
  #(
    Model(None),
    effect.from(fn(dispatch) {
      listen(fn(new_count) { 
        dispatch(GotNewCount(new_count))
      })
    })
  )
}

type Msg {
  GotNewCount(Int)
}

fn update(model: Model, msg: Msg) {
  case msg {
    GotNewCount(new_count) -&gt; #(Model(new_count), effect.none())
  }
}

fn view(model: Model) {
  let count_message =
    model.user_count
    |&gt; option.map(int.to_string)
    |&gt; option.unwrap("Getting user count...")

  html.p()
    |&gt; element.text_content(count_message)
}</code></pre><p>Run it on the server, serve it via websockets on <code>/user-count</code>, then add the following to our client&#8217;s view function:</p><pre><code>server_component.component()
  |&gt; server_component.route("/user-count")
  |&gt; element.children([
    html.p()
      |&gt; element.text_content("Getting user count...") 
  ])</code></pre><p>Lustre will make it happen so that our user count will travel from the server into the client, accurately rendered. No need to sync state, everything comes from the source.</p><p>In my opinion, even after considering all the good that is Gleam, MVU, and Lustre&#8217;s implementation of it, this is the biggest advantage of them all. </p><p>Whenever you use HTMX or LiveView, there always comes a time when you need to &#8220;sprinkle some JavaScript&#8221;. While sprinkling some JavaScirpt is much better than writing everything in JavaScript, writing none is best.</p><p>This is the power of Gleam&#8217;s Lustre. You have all the advantages of a single page application, <em>and</em> of server side components. You pick the right approach, and use the same exact tool to fulfill it.</p><p>I hear you, HTMX people calling into the past: &#8220;make the chat messages server side too&#8221;. Nope. Not going to happen. At least not the way you think it will.</p><p>You see, the chat <strong>is meaningful even when the user is offline</strong>. Even if you make it online-only, there&#8217;s a problem moving the chat messages functionality to the server. How will we handle a chat message that was just sent? The server can&#8217;t render a <code>Sending</code> state because it&#8217;s still sending &#8212; it doesn&#8217;t know it exists!</p><p>&#8220;Sprinkle some JavaScript!&#8221;</p><p>Yes, the current solution is to have some mix of client and server side code, and do the syncing manually, just for that one bit.</p><p>Or is it?</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Nestful&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Introducing OmniMessage</h2><p>OmniMessage is a library created to answer this exact problem. It is composed of a Lustre extension on the client side, and server utilities (including another, optional, Lustre extension) on the server side.</p><p>After you set those two up a subset of messages, at your discretion, will dispatch in <strong>both the client and the server</strong>. You can think of this as a more blissful RPC.</p><p>Here&#8217;s the boilerplate:</p><pre><code>import omnimessage_lustre as omniclient

// This just converts to/from JSON
let encoder_decoder =
  omniclient.EncoderDecoder(
    // this converts TO JSON
    fn(msg) {
      case msg {
        // Encode a certain subset of messages
        ClientMessage(message) -&gt; Ok(shared.encode_client_message(message))
        // Return Error(Nil) for messages you don't want to send out
        _ -&gt; Error(Nil)
      }
    },
    // this converts FROM JSON
    fn(encoded_msg) {
      shared.decode_server_message(encoded_msg)
      |&gt; result.map(ServerMessage)
    },
  )

// This creates an extended Lustre component
omniclient.component(
  init,
  update,
  view,
  dict.new(),
  encoder_decoder,
  transports.http("http://localhost:8000/omni-http", option.None, dict.new()),
  TransportState,
)</code></pre><p>The &#8220;biggest&#8221; chunk of work is encoding and decoding, but that&#8217;s something you need to do anyway. Even pure server components eventually need to save to a database.</p><p>If we setup OmniMessage like that, our original Lustre app could reuse <strong>all </strong>the client-side messages we wrote at the beginning of this post, and the server will reply and override our <code>Sending</code> chat messages when they are received.</p><p>Yes, yes, I know you want to see the code, but it really is the same. Here, look:</p><pre><code>// MODEL ---------------------------------------------------------------

pub type Model {
  Model(chat_msgs: dict.Dict(String, ChatMessage), draft_content: String)
}

fn init(_initial_model: Option(Model)) -&gt; #(Model, effect.Effect(Msg)) {
  #(Model(dict.new(), draft_content: ""), effect.none())
}

// UPDATE --------------------------------------------------------------

pub type Msg {
  UserSendDraft
  UserScrollToLatest
  UserUpdateDraftContent(String)
  ClientMessage(ClientMessage)
  ServerMessage(ServerMessage)
  TransportState(transports.TransportState(json.DecodeError))
}

fn update(model: Model, msg: Msg) -&gt; #(Model, effect.Effect(Msg)) {
  case msg {
    // Good old UI
    UserUpdateDraftContent(content) -&gt; #(
      Model(..model, draft_content: content),
      effect.none(),
    )
    UserSendDraft -&gt; {
      #(
        Model(..model, draft_content: ""),
        effect.from(fn(dispatch) {
          shared.new_chat_msg(model.draft_content, shared.Sending)
          |&gt; shared.UserSendChatMessage
          |&gt; ClientMessage
          |&gt; dispatch
        }),
      )
    }
    UserScrollToLatest -&gt; #(model, scroll_to_latest_message())
    // Shared messages
    ClientMessage(shared.UserSendChatMessage(chat_msg)) -&gt; {
      let chat_msgs =
        model.chat_msgs
        |&gt; dict.insert(chat_msg.id, chat_msg)

      #(Model(..model, chat_msgs:), scroll_to_latest_message())
    }
    // The rest of the ClientMessages are exlusively handled by the server
    ClientMessage(_) -&gt; {
      #(model, effect.none())
    }
    // Merge strategy
    ServerMessage(shared.ServerUpsertChatMessages(server_messages)) -&gt; {
      let chat_msgs =
        model.chat_msgs
        // Omnimessage shines when you're OK with server being source of truth
        |&gt; dict.merge(server_messages)

      #(Model(..model, chat_msgs:), effect.none())
    }
    // State handlers - use for initialization, debug, online/offline indicator
    TransportState(transports.TransportUp) -&gt; {
      #(
        model,
        effect.from(fn(dispatch) {
          dispatch(ClientMessage(shared.FetchChatMessages))
        }),
      )
    }
    TransportState(transports.TransportDown(_, _)) -&gt; {
      // Use this for debugging, online/offline indicator
      #(model, effect.none())
    }
    TransportState(transports.TransportError(_)) -&gt; {
      // Use this for debugging, online/offline indicator
      #(model, effect.none())
    }
  }
}

</code></pre><p>See? Same logic. The only differences are that some types are wrapped so we could share them with the server, and instead of handling network errors directly we now handle them through a <code>TransportState</code> variant.</p><p>Other than that, you dispatch messages and the server replies as if it&#8217;s the same app.</p><p>You decide how to encode the messages, and what transport to send them through. As long as the server can understand those, you can use whatever server, in any language. Currently we have transports for HTTP and websockets, but any transport is possible. For example, you could write one to communicate with an Electron or Tauri backend.</p><p>Here are some examples utilizing <code>omnimessage_server</code>, meant for Gleam servers. Say you have this simple handler:</p><pre><code>fn handle(ctx: Context, msg: Msg) -&gt; Msg {
  case msg {
    ClientMessage(shared.UserSendMessage(message)) -&gt; {
      ctx |&gt; context.add_message(message)

      context.get_chat_messages(ctx)
      |&gt; shared.ServerUpsertMessages
      |&gt; ServerMessage
    }
    ClientMessage(shared.UserDeleteMessage(message_id)) -&gt; {
      ctx |&gt; context.delete_message(message_id)

      context.get_chat_messages(ctx)
      |&gt; shared.ServerUpsertMessages
      |&gt; ServerMessage
    }
    ClientMessage(shared.FetchMessages) -&gt; {
      context.get_chat_messages(ctx)
      |&gt; shared.ServerUpsertMessages
      |&gt; ServerMessage
    }
    ServerMessage(_) | Noop -&gt; Noop
  }
}</code></pre><p>Here&#8217;s how you&#8217;d use it in a Gleam HTTP server:</p><pre><code>use &lt;- omniserver.wisp_http_middleware(
  req,
  "/omni-http",
  encoder_decoder(),
  handle(ctx, _),
)</code></pre><p>Just give it the request, the path it should handle, the messages encoder/decoder and the handler from above, and it will:</p><ol><li><p>Decode incoming messages</p></li><li><p>Run them through the handler</p></li><li><p>Encode the result</p></li><li><p>Generate an HTTP response with it</p></li></ol><p>Need to send messages without the client initiating a request? Use websockets:</p><pre><code>["omni-pipe-ws"], http.Get -&gt;
  omniserver.mist_websocket_pipe(
    req,
    encoder_decoder(), // this is the same encoder_decoer
    handle(ctx, _), // the same message handler
    logger, // error handler
  )</code></pre><p>Have a more complex app and you need structure? Use a Lustre server component!</p><pre><code>["omni-app-ws"], http.Get -&gt;
  omniserver.mist_websocket_application(
    req,
    chat.app(), // lustre server component
    ctx, // flags for its init
    logger // error handler
  )</code></pre><p>This is how the server component will look like:</p><pre><code>// MODEL ---------------------------------------------------------------
pub type Model {
  Model(messages: dict.Dict(String, shared.ChatMessage), ctx: Context)
}

fn init(ctx: Context) -&gt; #(Model, effect.Effect(Msg)) {
  #(Model(messages: ctx |&gt; context.get_chat_msgs, ctx:), effect.none())
}

// UPDATE --------------------------------------------------------------

pub type Msg {
  ClientMessage(ClientMessage)
  ServerMessage(ServerMessage)
}

pub fn update(model: Model, msg: Msg) {
  case msg {
    ClientMessage(shared.UserSendMessage(message)) -&gt; #(
      model,
      effect.from(fn(dispatch) {
        model.ctx |&gt; context.add_message(message)

        ctx
        |&gt; context.get_chat_msgs
        |&gt; shared.ServerUpsertMessages
        |&gt; ServerMessage
        |&gt; dispatch
      }),
    )
    ClientMessage(shared.UserDeleteMessage(message_id)) -&gt; #(
      model,
      effect.from(fn(dispatch) {
        model.ctx |&gt; context.delete_message(message_id)

        ctx
        |&gt; context.get_chat_msgs
        |&gt; shared.ServerUpsertMessages
        |&gt; ServerMessage
        |&gt; dispatch
      }),
    )
    ClientMessage(shared.FetchMessages) -&gt; #(
      model,
      effect.from(fn(dispatch) {
        get_messages(model.ctx)
        |&gt; shared.ServerUpsertMessages
        |&gt; ServerMessage
        |&gt; dispatch
      }),
    )
    ServerMessage(shared.ServerUpsertMessages(messages)) -&gt; #(
      Model(..model, messages:),
      effect.none(),
    )
  }
}</code></pre><p>It&#8217;s like having the same app spread across two different files.</p><p>OmniMessage is still very young and is missing some important features, but the vision is clear &#8212; it is the last piece in the trifecta that is zen state management:</p><ol><li><p>Client state &#8212; solved by MVU</p></li><li><p>Server state &#8212; solved by server components/LiveView approach</p></li><li><p>Hybrid state &#8212; solved by OmniState</p></li></ol><p>The hybrid state OmniMessage represents is a very sharp sword that can be tricky to wield without a clear separation of concerns. This is why OmniMessage shines when a single party is the source of truth, since we can simply override the other party&#8217;s state every time a message arrives. This gives us all the benefits of OmniMessage without the <s>beehive</s> hornets nest that is carefully merging state.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p><p>This is why Lustre is the end game of frontend development. It collected all of the right solutions and wrapped them up in the nicest, gleamy package.</p><p>Full, working, code is available in the <a href="https://github.com/weedonandscott/omnimessage/tree/master/example">OmniMessage repository</a>.</p><p>Here is the OmniMessage documentation:</p><p><a href="https://hex.pm/packages/omnimessage_server">https://hex.pm/packages/omnimessage_server</a></p><p><a href="https://hex.pm/packages/omnimessage_lustre">https://hex.pm/packages/omnimessage_lustre</a></p><p></p><p>And don&#8217;t forget to check out <a href="https://nestful.app">Nestful</a>! Not only is Nestful written in Gleam (the new &#8220;written in Rust&#8221;), but it actually has novel ways to manage your time:</p><p><a href="https://nestful.app">https://nestful.app</a></p><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/p/gleams-lustre-is-frontend-developments?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.nestful.app/p/gleams-lustre-is-frontend-developments?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Nestful&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Or you could use a CRDT (which we do, but that&#8217;s a story for another post)</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[List-Taking and Note-Taking]]></title><description><![CDATA[Why you should do both]]></description><link>https://blog.nestful.app/p/on-list-taking-and-note-taking</link><guid isPermaLink="false">https://blog.nestful.app/p/on-list-taking-and-note-taking</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Thu, 24 Oct 2024 14:56:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!aZOI!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13336315-f7a5-451b-ae0a-2cf7a1f81850_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p></p><p><a href="https://nestful.app/">Nestful</a> is very list-centric. Even the Kanban and Agenda views are just horizontal lists of vertical lists, and Nestful&#8217;s free-form text, the description field, is very limited. This avoidance in on purspose, a result of trial and error.</p><p>Before building Nestful, I had used some note-based competitors only to realize they weren&#8217;t competitors at all. Note-taking is a form of knowledge organization that is almost entirely orthogonal to list-taking.</p><p>When I tried using note-taking solutions exclusively, I found myself trying to recreate the functionality of list-taking inside of a note. While it is technically possible, it breaks down fairly early.</p><h2>The Role of Notes</h2><p>Notes are a collection of sections<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> that <strong>benefit from being a single piece of prose</strong>.</p><p>An essay such as this blog post is the immediate example, where each paragraph is part of the essay and although it has some meaning by itself, it is significantly reduced.</p><p>Job listings, reviews, pitch documents, diary entries, and summaries are more examples that fit note-taking well. They consist of sections that form a whole, with each section losing significance when in isolation<strong>.</strong></p><h2>The Role of Lists</h2><p>Lists are the exact opposite. They are a collection of items<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> that <strong>preserve their meaning without being connected by prose.</strong></p><p>The immediate list example is a shopping list, where each item has to be acquired regardless of the others.</p><p>I know what you&#8217;re thinking &#8212; a grocery list may have items dependant on one another if they are part of the same recipe. For example, if I want to make cookies and there aren&#8217;t any eggs available, I won&#8217;t need the sugar.</p><p>As long as you want to make cookies, you&#8217;re going to need sugar and eggs. The lack of eggs does not cancel the sugar, it just means that you have to look for eggs elsewhere. If eggs are hard to find, you may give up on making cookies, but that&#8217;s not sugar being dependent on eggs, that&#8217;s the ingredients depending on the recipe.</p><p>Even if you don&#8217;t think about it that way, a grocery list is a list of lists. Every item in that list is going to be used in the preparation of food, and therefore is part of a recipe.</p><p>This is where note-taking doesn&#8217;t fit. Notes cannot differentiate independent sections from interconnected ones, something that is explicit in lists.</p><p>Nestful uses that list-taking extensively. If you have lists of tasks nested into one another, Nestful allows you to &#8220;bubble up&#8221; due items by setting a due date. No matter how deep a task is nested, an agenda view will show it to you when it&#8217;s due. This is made possible by a list&#8217;s explicit division into items. In our grocery list example, one could add recipes, and the groceries will &#8220;bubble up&#8221; to form the list.</p><h2>It&#8217;s a Spectrum</h2><p>The division of content into lists and notes is not binary. Some fit into both.</p><p>While in my opinion a screenplay&#8217;s outline <a href="https://blog.nestful.app/p/ways-to-use-nestful-outlining-anime">benefits tremendously</a> from being a list, and should only become prose in the case of writing a treatment (or the actual screenplay). Some screenwriters prefer to outline as prose. That is fine.</p><p>The nice thing about it being a spectrum is that if you have content that&#8217;s not a clear fit for either, either will work, as it&#8217;s probably in the middle of the spectrum.</p><p>Use the right tool for the job.<br></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>It used to be just paragraphs, which are sections of just text, but since then sections have expanded to include various other forms of data presentation, such as tables or media.</p><p></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Although list items and note sections are technically different, you can think of them as the same thing for the sake of this essay.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[How to Not Be Overwhelmed]]></title><description><![CDATA[Divide and conquer, spontaneously]]></description><link>https://blog.nestful.app/p/how-to-not-be-overwhelmed</link><guid isPermaLink="false">https://blog.nestful.app/p/how-to-not-be-overwhelmed</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Mon, 21 Oct 2024 11:14:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Dy0i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Note: This is about <a href="https://Nestful.app">Nestful</a>, a list-taking app, but the principles can be applied anywhere. Please take it with you to the tool you&#8217;re using!</em></p><p>I often hear being overwhelmed is a major roadblock to productivity, but it shouldn&#8217;t be that way. In fact &#8212; it&#8217;s easy to avoid.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Nestful&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>To understand the solution, we first must understand the problem. While seemingly straightforward, there&#8217;s a nuance we must take to heart:</p><p>We can be overwhelmed by the length of a list <em>and</em> the perceived size of an item.</p><p>Therefore we need to solve both for a list with a large number of small items and  a list with one huge item.</p><p>The latter is easier, so let&#8217;s start there.</p><h2>Divide and Conquer</h2><p>Any single task you find overwhelming is divisible into smaller tasks.</p><p>That is a law of nature that is eternally true. <em>Why</em> it is true is a tale for another day. Do email me if you&#8217;re interested.</p><p>If you ever happen onto a task that you find indivisible, you must check your premises. Any violation of the law above cannot exist. The case of the unknown is the easiest to split, as the first subtask will be to figure out how split to the seemingly indivisible task. That subtask is also divisible.</p><p>Nestful is meant for such usage and excels at it. To illustrate this, let&#8217;s look at how I learned Japanese.</p><p>First, I had this language learning list:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Dy0i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Dy0i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png 424w, https://substackcdn.com/image/fetch/$s_!Dy0i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png 848w, https://substackcdn.com/image/fetch/$s_!Dy0i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png 1272w, https://substackcdn.com/image/fetch/$s_!Dy0i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Dy0i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png" width="668" height="363" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:363,&quot;width&quot;:668,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26140,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Dy0i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png 424w, https://substackcdn.com/image/fetch/$s_!Dy0i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png 848w, https://substackcdn.com/image/fetch/$s_!Dy0i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png 1272w, https://substackcdn.com/image/fetch/$s_!Dy0i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59471ee2-4df2-47ea-aea3-eca918c04eba_668x363.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That&#8217;s not very useful as &#8220;Japanese&#8221; is hardly an innocent, small task. Splitting was due.</p><p>I learn mostly through content consumption, but this item was strictly for the &#8220;chores&#8221; part of learning. Here&#8217;s how it looked after I divided Japanese into subtasks:<br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZV1H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40f2640-d96a-4d66-9c27-f2b2d21eced5_1290x494.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZV1H!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40f2640-d96a-4d66-9c27-f2b2d21eced5_1290x494.png 424w, https://substackcdn.com/image/fetch/$s_!ZV1H!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40f2640-d96a-4d66-9c27-f2b2d21eced5_1290x494.png 848w, https://substackcdn.com/image/fetch/$s_!ZV1H!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40f2640-d96a-4d66-9c27-f2b2d21eced5_1290x494.png 1272w, https://substackcdn.com/image/fetch/$s_!ZV1H!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40f2640-d96a-4d66-9c27-f2b2d21eced5_1290x494.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZV1H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40f2640-d96a-4d66-9c27-f2b2d21eced5_1290x494.png" width="1290" height="494" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a40f2640-d96a-4d66-9c27-f2b2d21eced5_1290x494.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:494,&quot;width&quot;:1290,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:54455,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZV1H!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40f2640-d96a-4d66-9c27-f2b2d21eced5_1290x494.png 424w, https://substackcdn.com/image/fetch/$s_!ZV1H!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40f2640-d96a-4d66-9c27-f2b2d21eced5_1290x494.png 848w, https://substackcdn.com/image/fetch/$s_!ZV1H!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40f2640-d96a-4d66-9c27-f2b2d21eced5_1290x494.png 1272w, https://substackcdn.com/image/fetch/$s_!ZV1H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40f2640-d96a-4d66-9c27-f2b2d21eced5_1290x494.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><br>But reading 8 books is also not a very consumable task. So we nest deeper:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!d8Iz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0949098d-b0ef-47dc-957e-7ba7f85fd8fe_2213x914.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!d8Iz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0949098d-b0ef-47dc-957e-7ba7f85fd8fe_2213x914.png 424w, https://substackcdn.com/image/fetch/$s_!d8Iz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0949098d-b0ef-47dc-957e-7ba7f85fd8fe_2213x914.png 848w, https://substackcdn.com/image/fetch/$s_!d8Iz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0949098d-b0ef-47dc-957e-7ba7f85fd8fe_2213x914.png 1272w, https://substackcdn.com/image/fetch/$s_!d8Iz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0949098d-b0ef-47dc-957e-7ba7f85fd8fe_2213x914.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!d8Iz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0949098d-b0ef-47dc-957e-7ba7f85fd8fe_2213x914.png" width="1456" height="601" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0949098d-b0ef-47dc-957e-7ba7f85fd8fe_2213x914.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:601,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:273639,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!d8Iz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0949098d-b0ef-47dc-957e-7ba7f85fd8fe_2213x914.png 424w, https://substackcdn.com/image/fetch/$s_!d8Iz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0949098d-b0ef-47dc-957e-7ba7f85fd8fe_2213x914.png 848w, https://substackcdn.com/image/fetch/$s_!d8Iz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0949098d-b0ef-47dc-957e-7ba7f85fd8fe_2213x914.png 1272w, https://substackcdn.com/image/fetch/$s_!d8Iz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0949098d-b0ef-47dc-957e-7ba7f85fd8fe_2213x914.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is more like it. For me, the size of these items is not overwhelming. If you still find them too large, remember: <strong>any task you find overwhelming is splittable into smaller tasks</strong>. Even if you don&#8217;t know the books, you&#8217;ll have a task to find the books. And if you don&#8217;t know how to look for them, you&#8217;ll have a task to ask a friend or learn how to use Google.</p><p>Eventually, you are going to reach a small enough task.<br></p><p>But now we have a lot of small tasks, which can cause the exact same symptoms. This is where Spontaneuous Productivity comes in.</p><h2>Creation Offload</h2><p>If you&#8217;ve used Nestful before, you know that in addition to the regular views in the above screenshots (list, kanban, etc), there&#8217;s also a special one called <strong>agenda</strong>.</p><p>Agenda view has a special trick up its sleeve: it will &#8220;bubble up&#8221; due items, even if they are nested deep inside your task tree:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xJIS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe2273d9-a260-4343-b150-1a119410db0e_764x451.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xJIS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe2273d9-a260-4343-b150-1a119410db0e_764x451.png 424w, https://substackcdn.com/image/fetch/$s_!xJIS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe2273d9-a260-4343-b150-1a119410db0e_764x451.png 848w, https://substackcdn.com/image/fetch/$s_!xJIS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe2273d9-a260-4343-b150-1a119410db0e_764x451.png 1272w, https://substackcdn.com/image/fetch/$s_!xJIS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe2273d9-a260-4343-b150-1a119410db0e_764x451.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xJIS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe2273d9-a260-4343-b150-1a119410db0e_764x451.png" width="764" height="451" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fe2273d9-a260-4343-b150-1a119410db0e_764x451.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:451,&quot;width&quot;:764,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:54509,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xJIS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe2273d9-a260-4343-b150-1a119410db0e_764x451.png 424w, https://substackcdn.com/image/fetch/$s_!xJIS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe2273d9-a260-4343-b150-1a119410db0e_764x451.png 848w, https://substackcdn.com/image/fetch/$s_!xJIS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe2273d9-a260-4343-b150-1a119410db0e_764x451.png 1272w, https://substackcdn.com/image/fetch/$s_!xJIS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe2273d9-a260-4343-b150-1a119410db0e_764x451.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The above screenshots show part of my home page, set to the agenda display, showing the due item &#8220;JPDB&#8221;, even though that item is inside &#8220;Japanese&#8221;, which is in turn inside &#8220;Languages&#8221;.</p><p>This is the secret to not being overwhelmed by the quantity. The length of the list is only overwhelming <em>if you have to deal with the entire list</em>. That is because having to deal with a big list as a list, is basically the same as dealing with a large item.</p><p>What we did here is we offloaded the mental load to the  list&#8217;s creation by dividing it into doable tasks and setting a due date where needed. This means that now, even if the list is huge (and mine is), you don&#8217;t have to care &#8212; just do the thing at the top.</p><p>Even if you suck at creating a good list (and you probably will, at the beginning), it&#8217;s still going to work just fine. Editing a list built with such intent is a breeze, mostly just moving things around when priorities change.</p><p>If you&#8217;re interested in learning more about this approach, read our essay on <a href="https://blog.nestful.app/p/spontaneous-productivity">Spontaneous Productivity</a>.</p><h2>That&#8217;s it!</h2><p>The best solutions are the simplest solutions, since those are the ones we are most likely to do.</p><p>Sometimes, though, there aren&#8217;t simple solutions, so just split the problem.</p><p>If you found it useful, please consider supporting by giving <a href="https://Nestful.app">Nestful</a> a try and sharing this essay.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Nestful&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Why I Rewrote Nestful in Gleam]]></title><description><![CDATA[Going away from TypeScript was easy, the question was -- where to?]]></description><link>https://blog.nestful.app/p/why-i-rewrote-nestful-in-gleam</link><guid isPermaLink="false">https://blog.nestful.app/p/why-i-rewrote-nestful-in-gleam</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Wed, 09 Oct 2024 11:36:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!aZOI!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13336315-f7a5-451b-ae0a-2cf7a1f81850_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Yesterday I completed a partial rewrite of <a href="https://nestful.app">Nestful</a> in <a href="https://gleam.run">Gleam</a>, a relatively young immutable functional language that compiles to both Erlang and JavaScript.</p><p>This post tells the story of why I wanted to leave TypeScript behind, and why I chose Gleam to replace it.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Nestful&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>A Need Rises</h2><p>Nestful is a list-taking app with the promise to consolidate all of the different things competing for its users&#8217; time. Those things (work, chores, musings, etc) would usually be stored in siloes &#8212;GitHub for personal projects, Trello for house chores, notes on life in a notes app, and so on. That&#8217;s great for prioritizing within a project, but how do you prioritize the projects themselves?</p><p>Nestful was born to solve this problem for me. The premise is simple: Nestful is a list of items, and items can be inside other items. That way I could have a list of my projects, prioritize them, and when it&#8217;s time to prioritize the project itself, I click the project&#8217;s item and inside there&#8217;s its own specific items, relating to its completion.</p><p>These were my main requirements from Nestful:</p><ol><li><p>Work completely offline, with composable sync (more on that later)</p></li><li><p>Fast iteration so I could dogfood it quickly</p></li><li><p>Work on all my devices</p></li></ol><p>Web it was.</p><p>I chose Vue + TypeScript because I was the most fluent with it, but it could easily have been any other client-side web technology and this post would have stayed mostly the same.</p><h2>TypeScript Frustrations</h2><p>As with every clean-slate project, the beginning was a breeze. I got started relatively quickly, but as I progressed and Nestful grew, problems started popping up.</p><h3>A Language Within a Language</h3><p>The types part of TypeScript is a language in and of itself. First you program the actual business logic, then you have to spend 10% of the time that took to wrangle types to be correct. The worst part of it?</p><p>When you&#8217;re done, you still don&#8217;t have confidence that it&#8217;s correct.</p><p>I did not delve deep enough into the typing system of TypeScript to know if it&#8217;s psychological. Maybe it is mathematically proven that once TypeScript compiles without any `any`s, it&#8217;s safe.</p><p>Maybe. I don&#8217;t care.</p><p>The way I see it, a language has to justify a learning curve. Learning to navigate Rust&#8217;s convoluted (at times) syntax is rewarded by very impressive compile time guarantees. </p><p>What does TypeScript give you that is worth the time it demands, in learning and maintenance?</p><h3>Inherited Difficulties</h3><p>Those problems with TypeScript are inherited and compounded when using a library. I used to spend 2-3 days when upgrading the client-side database I used at the time because for some reason, may it be TypeScript itself or the library&#8217;s author investment, the type system did not supply enough guarantees.</p><p>This is compounded when there&#8217;s a bug in a 3rd party library, or that I need to understand the inner-workings of one. I have become fairly adept in understanding other people&#8217;s code, especially in TypeScript, yet it is still more intensive of a task compared to a nicer, simple language like Gleam.</p><h3>Error Handling</h3><p>After Rust exposed me to the concept of errors as values, I could not go back. It was so much more ergonomic and resulted in such a better end product that I started sorely missing it. Although this is a Javascript problem first and foremost, its supersets can solve it, and some did try.</p><p>There are several attempts to make &#8220;Functional TypeScript&#8221;, but I did not like them. First, there was no nice solution to handle those returned errors, with the glaring lack of pattern matching. More than that, though &#8212; eventually, when the kitchen sink is full enough, the tools are too suffocated to be useful.</p><h3>TypeScript is No Fun</h3><p>Anything I do should be as enjoyable as possible, and when it comes down to it, TypeScript is just not fun.</p><p>Some things are just destined to suck, like the recent clearing up of a clogged sewer at the house. Programming Nestful is not one of them.</p><p>This is also important to its success as an application &#8212; as Nestful grew and will grow, refactors were and will be needed. Every TypeScript refactor resulted in more bugs. Gleam refactors resulted in less. More on that below.</p><h2>&#8220;Compiled to WASM&#8221;</h2><p>I am a relatively thorough person. As I got more frustrated with the stack I had, I began going one by one over the possible alternatives. There&#8217;s a <a href="https://github.com/appcypher/awesome-wasm-langs">lovely repository on GitHub</a> tracking languages that compile to WebAssembly, so I started looking at each, one by one.</p><p>They all had one problem in common, though. Although WASM runs in the browser, it will demand a practically complete rewrite of Nestful, due to interoperability issues that will inevitably arise.</p><p>The solution is then to strictly compile to JavaScript.</p><p>I can&#8217;t remember which list I looked at at the time (maybe the one on the <a href="https://github.com/jashkenas/coffeescript/wiki/List-of-languages-that-compile-to-JS">CoffeScript wiki</a>?), but in the end I have narrowed it down to:</p><ul><li><p>Dart</p></li><li><p>F#</p></li><li><p>OCaml</p></li></ul><p>Dart was easy to pass on, even though it&#8217;s one I already knew, and on paper &#8212; the most fitting replacement &#8212; especially when adding a lot of the functional-style features I was looking for. However, it suffers from the crowded kitchen sink problem, and&#8230; Google makes it. I like using Flutter a fair bit, but I would rather split my eggs over multiple baskets, thank you very much.</p><p>F# and OCaml were pretty close. F# looked more approachable to me, but the ecosystem was fairly slow going, and I don&#8217;t paticularly enjoy working with Microsoft tools. OCaml seems to be back on track, and used in some major production codebases, but the syntax was not as familiar to me, and I got mixed signals about the rejuvenation they claim they have.</p><p>I have a soft-rule that says that in every new project I start, I must learn something new to the extent my bandwidth allows. Even though I decided not to go with either F# or OCaml this time, I&#8217;ll be happy if they turn out to be a good fit in a future project.</p><p>Then Gleam hit 1.0, which I discovered when YouTube recommended me a <a href="https://www.youtube.com/watch?v=9mfO821E7sE">Primeagen video</a>. Before, I probably skimmed over Gleam and passed for it being too young. Having it reach stability with the community momentum to compensate for being young relaxed my ecosystem worries (a little), and I took it for a spin.</p><h2>Star of the Show</h2><p>Gleam has quickly turned out to be an excellent choice made at the right time. It streamlined my development even though I had to write a lot of (FFI) boilerplate to get going.</p><h3>As Simple as it Gets</h3><p>Gleam has a simple-language philosophy. This means that the language itself is fairly small and contained (there&#8217;s no `if`!), which in turn makes it very easy to learn, and most importantly, extremely easy to understand 3rd party code.</p><p>Even with a language this young and some things that are not fully ready, I had a much better time than doing the same task with TypeScript.</p><h3>Easy Refactor</h3><p>As Nestful grows refactors are going to be needed. &#8220;Composable sync&#8221;, mentioned in the requirements, is an example of that.</p><p>If Nestful promises to be a place that hosts all the different things competing for my time, it better do that for things that need more than a list to be managed. Some projects do need the likes of Linear or GitHub, and I would like to be able to pull items from there and transparently display them to the user to be prioritized. They don&#8217;t need all of GitHub&#8217;s features to prioritize between fixing bug #233 and putting the car in the garage. When they actually reach fixing that bug they can click the item and Nestful will lead them right to GitHub.</p><p>The data layer is abstracted enough to achieve this, but you don&#8217;t really know what you need until you need it, especially if you avoid premature optimization like I do.</p><p>I am confident with Gleam having my back when it&#8217;s time to modify the data layer, helping me refactor correctly, even when it&#8217;s tricky. For example, Nestful has a feature that &#8220;bubbles up&#8221; items. It&#8217;s currently only used to show use deeply-nested due items in a single view (to prioritize between "bug #233 somewhere inside a project and &#8220;Put car in garage&#8221; somewhere inside house chores). Expanding this to more use cases is not trivial, and Gleam is here to help.</p><h3>Fluffy and Cozy</h3><p>Gleam is fun to write in. It&#8217;s a calming language if there ever was one. It&#8217;s soft to read and write (you&#8217;ll get it when you try it) and is overall a relaxing experience. That&#8217;s mostly thanks to its syntax choices.</p><p>Beyond that, it supplies the usual functional/immutable features you&#8217;d expect from a first release of such a language. Namely, to my liking, no nulls, errors as values, and pattern matching.</p><h3>Enter Vleam</h3><p>The cherry on the top is that I could adopt Gleam incrementally. I could add it piece by piece, replacing the TypeScript in my services, composables, and components. For that last one to be easy, I wrote <a href="https://github.com/vleam/vleam">Vleam</a>.</p><p>Vleam is a set of tools (Vite plugin, Vue FFI, and LSP) that allows the usage of Gleam in Vue single-file components with full LSP and hot-reload support. Give it a try if you&#8217;re looking for a change and running Vue yourself. Easily done one component at a time.</p><h2>Not Perfect Yet, But Overall Right</h2><p>Gleam doesn&#8217;t have everything nailed quite yet. Some LSP features are sorely lacking (it&#8217;s rename for me), and the language could use a bit of introspection and reflection so I could avoid a neverending unwrapping of types and the chore of serialization.</p><p>Even with those rough spots, though, Gleam is still a great choice. It&#8217;s a breath of fresh air over the chore that is TypeScript, and is making big strides with every release.</p><p>Next, I&#8217;ll give it a shot on the backend. </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Nestful&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Ways to use Nestful: Outlining Anime]]></title><description><![CDATA[What is "Kishoutenketsu" and how outlining using this popular Asian story structure is especially effective on Nestful]]></description><link>https://blog.nestful.app/p/ways-to-use-nestful-outlining-anime</link><guid isPermaLink="false">https://blog.nestful.app/p/ways-to-use-nestful-outlining-anime</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Tue, 08 Oct 2024 11:31:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!e6BL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>IMPORTANT: I use the pilot of The Promised Neverland as an example, so</em> <em><strong>SPOILERS</strong></em> <em>ahead. Do go watch it before reading this</em></p><p>Recently a software I make called Nestful had its desktop app released, and while I made it for a whole different reason, I use it <strong>a lot</strong> for outlining screenplays.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Nestful&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>Nestful and Kishoutenketsu</h1><p>Nestful was initially built to fill one purpose: be a task app that lets me compare tasks across different areas of my life. My daily job, my screenplays and my chores all compete for my time, yet are all siloed away, unable to be prioritized. Nestful solves this by nesting items in one another, allowing projects to be item of the same list, and inside have their own list to manage said project.</p><p>This turned out to be extremely useful for outlining, especially the Japanese-style structure of Kishoutenketsu, which is often used for Anime.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!e6BL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!e6BL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png 424w, https://substackcdn.com/image/fetch/$s_!e6BL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png 848w, https://substackcdn.com/image/fetch/$s_!e6BL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png 1272w, https://substackcdn.com/image/fetch/$s_!e6BL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!e6BL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png" width="1456" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!e6BL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png 424w, https://substackcdn.com/image/fetch/$s_!e6BL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png 848w, https://substackcdn.com/image/fetch/$s_!e6BL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png 1272w, https://substackcdn.com/image/fetch/$s_!e6BL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F41c58f2a-90ec-4011-ad8d-85f0de6b753a_1703x842.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For those who are unfamiliar with Kishoutenketsu, it's a 4-act structure popular in Asia which originated in Japan, and is comprised of 4 acts. In Japanese they are called:</p><ul><li><p>&#36215; Ki - Introduction</p></li><li><p>&#25215; Shou - Development</p></li><li><p>&#36578; Ten - Turn (often translated as twist, but isn't necessarily a twist in the western sense)</p></li><li><p>&#32080; Ketsu - Resolution</p></li></ul><p>Kishoutenketsu is fairly flexible, with writers taking big liberties in the lengths of acts. The Promised Neverland employes a fairly common and clear division of 1/8, 1/2, 1/4, 1/8 for each of the acts respectively.</p><h1>Outlining The Promised Neverland</h1><p>I'll link below some videos about the structure if you want to learn more, but for now -- let's use The Promised Neverland's outline as an example:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Lp4S!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05073fd7-00a3-467d-9783-5916cc3e580e_1703x842.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Lp4S!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05073fd7-00a3-467d-9783-5916cc3e580e_1703x842.png 424w, https://substackcdn.com/image/fetch/$s_!Lp4S!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05073fd7-00a3-467d-9783-5916cc3e580e_1703x842.png 848w, https://substackcdn.com/image/fetch/$s_!Lp4S!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05073fd7-00a3-467d-9783-5916cc3e580e_1703x842.png 1272w, https://substackcdn.com/image/fetch/$s_!Lp4S!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05073fd7-00a3-467d-9783-5916cc3e580e_1703x842.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Lp4S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05073fd7-00a3-467d-9783-5916cc3e580e_1703x842.png" width="1456" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/05073fd7-00a3-467d-9783-5916cc3e580e_1703x842.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Lp4S!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05073fd7-00a3-467d-9783-5916cc3e580e_1703x842.png 424w, https://substackcdn.com/image/fetch/$s_!Lp4S!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05073fd7-00a3-467d-9783-5916cc3e580e_1703x842.png 848w, https://substackcdn.com/image/fetch/$s_!Lp4S!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05073fd7-00a3-467d-9783-5916cc3e580e_1703x842.png 1272w, https://substackcdn.com/image/fetch/$s_!Lp4S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05073fd7-00a3-467d-9783-5916cc3e580e_1703x842.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>First, in Ki, we are getting literally introduced -- to the characters, and to their environment. This is pure exposition, and is done by contrasting the unknown (numbers, gate) with the known (the children are happy),</p><p>This expands in Shou, where we're shown the abilities and faults of every major character as part of tests, and play. Note that almost every sequence serves more than one duty. The tests show not only who are the smartest characters, but also foreshadow the importance of their intelligence to the institution they're in. I've used the Japanese name for the game on tag, Onigokko, because its literal translation is "demon pretend", the one who's "it" pretends to be a demon. Shou then transitions brilliantly into Conny's leaving, where we have more character reveal and more environment contrast.</p><p>Ten starts with Conny's forgotten stuffed bunny. Emma and Norman run after her with it, reach the forbidden gate from the cold open, and cross it. They explore the very different environment, and find dead Conny. They then hide from what they soon find out are actual demons, and we get the twist -- their raised in a farm as food food. In this case the twist is truly a twist as we know it in the west, but it doesn't have to be -- in fact, it's not sufficient. What's needed is a turn in the story. In this case, the twist changed the perspective. But it can be, for example, a significant turn of events that changes goals without revealing information, as in some of Miyazaki's films.</p><p>Ketsu is very straightforward, showing the characters dealing with the shock, and planning for the future -- tying in what they went through in the episode.</p><h1>Nesting Kishoutenketsu</h1><p>So far you may think that although Kishoutenketsu may be interesting, outlining it is no different than any other index card outline, and all I have in that screenshot is a digital version of that.</p><p>You would be right, until we click on an item in that outline. Here&#8217;s how it looks when we click &#8220;Ten&#8221;:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!b-V4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e071fb8-e338-4c8a-a99f-d0f1af90c25a_1275x834.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!b-V4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e071fb8-e338-4c8a-a99f-d0f1af90c25a_1275x834.png 424w, https://substackcdn.com/image/fetch/$s_!b-V4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e071fb8-e338-4c8a-a99f-d0f1af90c25a_1275x834.png 848w, https://substackcdn.com/image/fetch/$s_!b-V4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e071fb8-e338-4c8a-a99f-d0f1af90c25a_1275x834.png 1272w, https://substackcdn.com/image/fetch/$s_!b-V4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e071fb8-e338-4c8a-a99f-d0f1af90c25a_1275x834.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!b-V4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e071fb8-e338-4c8a-a99f-d0f1af90c25a_1275x834.png" width="1275" height="834" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e071fb8-e338-4c8a-a99f-d0f1af90c25a_1275x834.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:834,&quot;width&quot;:1275,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!b-V4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e071fb8-e338-4c8a-a99f-d0f1af90c25a_1275x834.png 424w, https://substackcdn.com/image/fetch/$s_!b-V4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e071fb8-e338-4c8a-a99f-d0f1af90c25a_1275x834.png 848w, https://substackcdn.com/image/fetch/$s_!b-V4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e071fb8-e338-4c8a-a99f-d0f1af90c25a_1275x834.png 1272w, https://substackcdn.com/image/fetch/$s_!b-V4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e071fb8-e338-4c8a-a99f-d0f1af90c25a_1275x834.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Everything in Nestful is an Item, and items can be put inside of other items. This is useful to us because Kishoutenketsu <strong>is highly fractal</strong>. The structure is nested in itself, and the Ten act is the perfect example for that, with an item in the list for each of the sub-acts: Ki, Shou, Ten, Ketsu.</p><p>Now we can change Ten to be a board as well, rename the items, and outline the inner-acts in the exact same way. This is how it looks:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!N37b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06b694e2-74c9-4848-a256-f5e21c802705_1652x1146.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!N37b!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06b694e2-74c9-4848-a256-f5e21c802705_1652x1146.png 424w, https://substackcdn.com/image/fetch/$s_!N37b!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06b694e2-74c9-4848-a256-f5e21c802705_1652x1146.png 848w, https://substackcdn.com/image/fetch/$s_!N37b!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06b694e2-74c9-4848-a256-f5e21c802705_1652x1146.png 1272w, https://substackcdn.com/image/fetch/$s_!N37b!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06b694e2-74c9-4848-a256-f5e21c802705_1652x1146.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!N37b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06b694e2-74c9-4848-a256-f5e21c802705_1652x1146.png" width="1456" height="1010" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/06b694e2-74c9-4848-a256-f5e21c802705_1652x1146.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1010,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!N37b!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06b694e2-74c9-4848-a256-f5e21c802705_1652x1146.png 424w, https://substackcdn.com/image/fetch/$s_!N37b!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06b694e2-74c9-4848-a256-f5e21c802705_1652x1146.png 848w, https://substackcdn.com/image/fetch/$s_!N37b!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06b694e2-74c9-4848-a256-f5e21c802705_1652x1146.png 1272w, https://substackcdn.com/image/fetch/$s_!N37b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06b694e2-74c9-4848-a256-f5e21c802705_1652x1146.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Flexibility and Beyond</h1><p>This nesting method is flexible in two ways.</p><p>The first is its useful to other structures, or even no structure. Eventually a screenplay is big parts composed of smaller parts, which are themselves composed of yet smaller parts. This is independent of any structure, and you can mix and match different structures and layouts inside of others.</p><p>The second is that it only takes a list to do, so it can be done in a variety of tools. I find Nestful to be the most ergonomic one, but there are many other apps that have at least some nesting capability, even if not as central as Nestful's.</p><p>I hope this was useful to you! Before I add the Kishoutenketsu videos I promised, did you maybe notice this post itself has a certain structure? Maybe even this very paragraph?</p><p>Anyway, here they are:</p><div id="youtube2-CxaahzwDwHk" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;CxaahzwDwHk&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/CxaahzwDwHk?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p><br></p><div id="youtube2-pPxjTVpY55w" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;pPxjTVpY55w&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/pPxjTVpY55w?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.nestful.app/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Nestful&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Spontaneous Productivity]]></title><description><![CDATA[The reasoning behind Nestful]]></description><link>https://blog.nestful.app/p/spontaneous-productivity</link><guid isPermaLink="false">https://blog.nestful.app/p/spontaneous-productivity</guid><dc:creator><![CDATA[Nestful]]></dc:creator><pubDate>Tue, 30 Jul 2024 17:30:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb869164f-425c-4573-8229-b40e2dc204de_961x405.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Spontaneous productivity is a lifestyle approach to making more of one's time. Specifically, to getting stuff done as efficiently as possible without stress or anxiety.</p><p></p><h2>Scheduling Sucks</h2><p>Before we get into the solution, we must understand the problem: scheduling. That is because scheduling, and prepare to be shocked, takes time.</p><p>Scheduling is spending time in order to then spend time later. Supposedly in a more efficient manner, but that is impossible. Just ask any software development team and they'll tell you the unavoidable truth: humans are bad at estimating future work.</p><p>This means you often have to spend more time fixing mistakes and accommodating for changes in the schedule you came up with to efficiently spend your time.</p><p>Note, that's before you even touch the thing you actually want to do.</p><p></p><h2>Solution Part I: The What</h2><p>To avoid this sort of meta-timespending, Spontaneous Productivity defines a clear goal: decide what to do only just before you do it.</p><p>To achieve said goal, we need an evergreen list of priorities. A list in which tasks are always positioned according to their urgency. With that kind of list, instead of having a varied calendar with time-set activities spread across, you're going to have something like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1jQA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe851e1b1-563e-4a43-b606-e200e2458ff4_623x820.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1jQA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe851e1b1-563e-4a43-b606-e200e2458ff4_623x820.png 424w, https://substackcdn.com/image/fetch/$s_!1jQA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe851e1b1-563e-4a43-b606-e200e2458ff4_623x820.png 848w, https://substackcdn.com/image/fetch/$s_!1jQA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe851e1b1-563e-4a43-b606-e200e2458ff4_623x820.png 1272w, https://substackcdn.com/image/fetch/$s_!1jQA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe851e1b1-563e-4a43-b606-e200e2458ff4_623x820.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1jQA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe851e1b1-563e-4a43-b606-e200e2458ff4_623x820.png" width="623" height="820" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e851e1b1-563e-4a43-b606-e200e2458ff4_623x820.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:820,&quot;width&quot;:623,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:36446,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1jQA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe851e1b1-563e-4a43-b606-e200e2458ff4_623x820.png 424w, https://substackcdn.com/image/fetch/$s_!1jQA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe851e1b1-563e-4a43-b606-e200e2458ff4_623x820.png 848w, https://substackcdn.com/image/fetch/$s_!1jQA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe851e1b1-563e-4a43-b606-e200e2458ff4_623x820.png 1272w, https://substackcdn.com/image/fetch/$s_!1jQA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe851e1b1-563e-4a43-b606-e200e2458ff4_623x820.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The purple-blue events are all the activities that depend on other people, other resources or only available at certain times (also called "deps"), and are scheduled are usual. The rest is Spontaneous Productivity time.</p><p>Whenever you reach SP time, you open your evergreen priorities list, and do the top item. No planning, no worrying, no rescheduling. Spontaneous.</p><p></p><h2>Solution Part II: The How</h2><p>Obviously, we can't use any random list to achieve this. We have to have some sort of method to build a list that's ordered by priorities and, more importantly, that is evergreen -- meaning, it stays ordered.</p><p>Now I have good news, and bad news. I'll start with the bad.</p><p>The bad news is there's no way to create such a list. The good news is we can get pretty damn close.</p><p>We need 3 things to make this list happen:</p><p>1.  Offload urgency indicators to task creation</p><p>2.  Automatically order list according to urgency indicators</p><p>3.  Be able to update said indicators</p><p>An urgency indicator is a piece of information, related to a task, that can be compared with equivalent pieces of information in other tasks. The best example is a due date. We can compare the due date of different task to see which one has less time remaining for completion.</p><p>There are many possible other indicators. For example, task completion percentage. A task that is mostly done is less important than one you haven't even started. Or maybe a consequences indicator can represent the cost of failing the task's completion. Not watching a movie you planned to is not as bad as missing an important job deadline. Especially if that job finances that movie streaming account.</p><p>Once we have a list of tasks with corresponding indicators we can order those tasks, and update that order as we update the tasks themselves. The way we sort that list depends on our own preferences, so no magic formula exists. However, here come the even better news.</p><p></p><h2>Solution Part III: The Tool</h2><p>Nestful is the first tool to implement the principle behind Spontaneous Productivity. While you can post-it your way through it, sooner or later it's going to get too complicated and slow to do. Thankfully, we have computers.</p><p>Nestful includes a set of useful features exactly for this. For example, Agenda view "bubbles up" all due sub-tasks so you won't miss anything, and arranges everything according to date. This is the due date indicator mentioned before.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_eNz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb869164f-425c-4573-8229-b40e2dc204de_961x405.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_eNz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb869164f-425c-4573-8229-b40e2dc204de_961x405.png 424w, https://substackcdn.com/image/fetch/$s_!_eNz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb869164f-425c-4573-8229-b40e2dc204de_961x405.png 848w, https://substackcdn.com/image/fetch/$s_!_eNz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb869164f-425c-4573-8229-b40e2dc204de_961x405.png 1272w, https://substackcdn.com/image/fetch/$s_!_eNz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb869164f-425c-4573-8229-b40e2dc204de_961x405.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_eNz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb869164f-425c-4573-8229-b40e2dc204de_961x405.png" width="961" height="405" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b869164f-425c-4573-8229-b40e2dc204de_961x405.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:405,&quot;width&quot;:961,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:80087,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_eNz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb869164f-425c-4573-8229-b40e2dc204de_961x405.png 424w, https://substackcdn.com/image/fetch/$s_!_eNz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb869164f-425c-4573-8229-b40e2dc204de_961x405.png 848w, https://substackcdn.com/image/fetch/$s_!_eNz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb869164f-425c-4573-8229-b40e2dc204de_961x405.png 1272w, https://substackcdn.com/image/fetch/$s_!_eNz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb869164f-425c-4573-8229-b40e2dc204de_961x405.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The general nestability of tasks allows you to basically make each task its own full-blown kanban board, if you have more complex things to manage. This is with the strict principle of keeping one single list, even if you have very complex tasks.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pvi3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F551f4bf6-6a29-4397-82c9-a476e1f49373_1728x856.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pvi3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F551f4bf6-6a29-4397-82c9-a476e1f49373_1728x856.png 424w, https://substackcdn.com/image/fetch/$s_!pvi3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F551f4bf6-6a29-4397-82c9-a476e1f49373_1728x856.png 848w, https://substackcdn.com/image/fetch/$s_!pvi3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F551f4bf6-6a29-4397-82c9-a476e1f49373_1728x856.png 1272w, https://substackcdn.com/image/fetch/$s_!pvi3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F551f4bf6-6a29-4397-82c9-a476e1f49373_1728x856.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pvi3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F551f4bf6-6a29-4397-82c9-a476e1f49373_1728x856.png" width="1456" height="721" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/551f4bf6-6a29-4397-82c9-a476e1f49373_1728x856.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:721,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:273229,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pvi3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F551f4bf6-6a29-4397-82c9-a476e1f49373_1728x856.png 424w, https://substackcdn.com/image/fetch/$s_!pvi3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F551f4bf6-6a29-4397-82c9-a476e1f49373_1728x856.png 848w, https://substackcdn.com/image/fetch/$s_!pvi3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F551f4bf6-6a29-4397-82c9-a476e1f49373_1728x856.png 1272w, https://substackcdn.com/image/fetch/$s_!pvi3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F551f4bf6-6a29-4397-82c9-a476e1f49373_1728x856.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Of course, there is much room to grow. From better repeatability options, to crazy things like bundling together tasks that happen in the same place, or using similar resources. I personally believe in the beauty of simplicity, so expect to see more things like the Agenda view mentioned above. A huge portion of the benefits hides among the simplest of solutions.</p><p>I hope you give Spontaneous Productivity, and in turn Nestful, a shot. If you don't like the latter, that's fine -- each has its own take. As for the former -- unless 90% of your day is meeting other people, you can make Spontaneous Productivity work. When you do, tell us how you did it so we can learn and improve our own approaches.</p><p>Please reach out with anything to contact@nestful.app</p>]]></content:encoded></item></channel></rss>