Jekyll2021-08-31T15:20:33+00:00https://startupinamonth.net/feed.xmlStartup in a MonthJoin me as I build 12 startups in 12 months using the Clojure language. I've never founded a startup, and I'm a Clojure newbie!Andy FryWhat I learned launching my first product, part 32021-05-28T00:00:00+00:002021-05-28T00:00:00+00:00https://startupinamonth.net/what-i-learned-pt-3<p><a href="/what-i-learned-pt-1/">Read part 1 of this series</a>
<br />
<a href="/what-i-learned-pt-2/">Read part 2 of this series</a></p>
<p>Welcome! This post is part of a blog series about things that I’ve learned from releasing my first product as a bootstrapped startup founder. It’s been a massively educational process for me, and I want to share my learnings for the benefit of others and to document the journey for myself.</p>
<p>These posts are all about <a href="https://picstory.studio">PicStory</a>, which is an app that lets you make cool photo stories and share them with anybody. It has three goals:</p>
<ul>
<li>
<p>to give you more creative freedom to create than Instgram or Google Photos,</p>
</li>
<li>
<p>to give you a simple and accessible interface to create stories, and finally</p>
</li>
<li>
<p>to make it easier to share them with friends and family by not locking your content into a walled garden where you have to have an account to view a story.</p>
</li>
</ul>
<p>Check it out! <a href="https://app.picstory.studio/#/feedback">And let me know what you think</a>.</p>
<p><img src="/assets/images/what-i-learned/picstory-logo.png" alt="Pic Story Logo" /></p>
<p>So, let’s dive into lesson three:</p>
<h2 id="its-easy-to-intentionally-waste-time-on-performance-and-cost-reduction">It’s easy to “intentionally” waste time on performance and cost reduction</h2>
<p>This was a big one for me. As a founder who is also building the product itself, I get hit from two different directions when it comes to the topic of performance and cost savings:</p>
<ul>
<li>as a programmer, it’s very easy to get sucked into a mindset that an app has to meet some minimum level of performance to have a chance of working in the real world<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</li>
<li>As a solo founder bootstrapping my apps on a limited budget, I’ve got a lot of mental pressure riding on the idea that I need to save money wherever I can<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup></li>
</ul>
<p>This came to a head with my choice of hosting provider. I initially wanted to go with Digital Ocean because I’m familiar with them. And in the 11th hour as I was getting ready to launch, I discovered that Linode can offer basically the same cloud server hosting as Digital Ocean, but for a marginally better price.</p>
<p>Right away, my mind started to turn over the prospect of switching to Linode. “This is an opportunity to save some money! And it’s a smart business move to make, right? This thing might be online for years, think of all the cash I could save. And after all, I’m launching a dozen apps this year, if you multiply out the factorial of how much these things will cost to host, that’s even more money saved!”</p>
<p>And so, after weeks of using Digital Ocean and after having become modestly entangled into all of the technical debt that comes along with using any cloud-based service, I found myself signing up for Linode. Researching Vagrant integrations with Linode. Debugging aforementioned integrations. Generally getting into the weeds and not making any headway on actually getting PicStory online.</p>
<p>These kinds of performance enhancements have a kind of opiate allure to them. Monthly server hosting costs? Reducing the size of CSS and JS files? These are all “easy”-ish problems with “easy”-ish solutions and clear benefits. The solutions to all of these things reside within the sphere of the technological, where I am most comfortable and familiar.</p>
<p>After spending some time on this Linode tangent, eventually it hit me: what am I <em>doing</em>? I don’t have a single user yet and already I’m worried about saving money on hosting? This is a problem for Future Andy, not Present Andy. Figure this out later. Hell, I could even figure it out <em>months</em> down the road, and I’d only have spent like $100 more than I would have otherwise. For now, I need to just <strong>get this thing online</strong> and then worry about the small stuff later.</p>
<p>And so I put my focus back to the task of just getting it out there, switched back to Digital Ocean, and got on with the more pressing and immediate issues!</p>
<h2 id="easing-up-on-the-reins">Easing up on the reins</h2>
<p>Since releasing Pic Story, and as I’ve been working on <a href="/mailfile-announcement/">Mail File</a>, I’ve been turning this question of hosting and expenses over in my head more. App infrastructure is something that I’ve never been particularly good at. Sure, I CAN set up a server from scratch. I HAVE done it. Some people even LIKE to do that kind of work. But it’s a massive source of stress for me. I’ve tried out a number of other products and strategies for minimizing that stress – Vagrant at first, a brief dalliance with Docker and Kubernetes – but there’s no silver bullet for any of it.</p>
<p>And so, having struggled with making smart infrastructure choices for a couple months and feeling stuck, I’m letting up the reins a little bit. I’ll be using Heroku hosting for Mail File. It’s several orders of magnitude more expensive than a DIY cloud server, and those expenses will only increase if I stay with Heroku or some other PaaS for future apps. However, if that additional expense means that I get to avoid some extra stressful work and some extra dumb errors and bugs, it has to be worth it, right?</p>
<p>This is a delicate one-man operation after all: anything that takes pressure off of my shoulders has to be worth the cost if it leaves me feeling lighter and nimbler.</p>
<p>That feeling of lightness, as I’m discovering, is really important. When I say “delicate one-man operation,” I really mean it. Work is an emotional rollercoaster no matter what you’re doing, but emotional disturbances hit way differently depending on what the job itself is like.</p>
<p>When I was working in a big 15-person team within a huge corporation, I could weather an emotional storm much more easily. If I was having a bad week, I could take my foot off the gas and coast when I needed to. It’s easy to camoflage yourself when your manager has 15 other people to keep track of and the CEO is in another timezone entirely. Once the coasting is done and you’ve had time to cope, you can jump right back in, and nobody’s the wiser.</p>
<p>When you’re on your own though, the game is completely different. An emotional problem which might have been manageable in a big corporation can bring all production to a screeching halt when there’s nobody around to take up your slack.</p>
<p>When you lose all momentum like that, it is a serious pain in the ass to get it back. You have to re-establish your rhythm, you have to reassess your deadlines, you have to put down the meta-stress about missing aforementioned deadlines, hell: you have to remember what the hell it was that you were even working on before everything came crashing down around your ears!</p>
<p>After going through this cycle a few times, it’s shown me that maintaining an even keel and maintaining consistency is crucial to my success and wellbeing. And if the price of that expense is higher hosting costs, then so be it.</p>
<p>So there’s my third lesson: find the balance between what you’re spending and what you’re getting out of it. The name of the game isn’t to get your product out for as little money as humanly possible. Keeping expenses down is important, but it’s not the whole story. What’s more important is to find the sweet triple point where productivity, well-being, and expenses all come together to form a harmonious whole. If you can unlock that riddle in your work, you’ve made it my friend!</p>
<h2 id="one-last-thing">One last thing</h2>
<p>Thanks for reading this! It’s a busy world, and it means a lot that you spent some of your your time and attention on what I’m doing.</p>
<p>Would you like to join my mailing list? I send out updates once a month, so you can stay informed on what’s going on, but not <em>too</em> informed 😊</p>
<form action="https://sendy.startupinamonth.net/subscribe" method="POST" accept-charset="utf-8">
<label for="name">Name</label><br />
<input style="background: white;" type="text" name="name" id="name" />
<br />
<label for="email">Email</label><br />
<input style="background: white;" type="email" name="email" id="email" /><br /><br />
<div style="margin-bottom: 1.5rem;">
<span class="gdpr-permission">
<input type="checkbox" name="gdpr" id="gdpr" />
<label for="gdpr"><strong>Marketing permission</strong>:</label>
</span>
<span>
I give my consent to contact me via email for the purpose of news, updates and marketing. If you wish to withdraw your consent, click the unsubscribe link at the bottom of our emails or send an email to <a href="mailto:andy@startupinamonth.net">andy@startupinamonth.net</a>.
</span>
</div>
<br /><br />
<div style="display:none;">
<label for="hp">HP</label><br />
<input type="text" name="hp" id="hp" />
</div>
<input type="hidden" name="list" value="8xpdlVrcgW8921MAwkzOi1vw" />
<input type="hidden" name="subform" value="yes" />
<div>
<button class="primary-cta" type="submit" name="submit" id="submit">Sign me up!</button>
</div>
</form>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>For example, <a href="https://www.marketingdive.com/news/google-53-of-mobile-users-abandon-sites-that-take-over-3-seconds-to-load/426070/">Google research from 2016</a> showed that <em>over half of your visitors</em> will abandon your page if it takes longer than three seconds to load! Yikes. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>This is important of course: money doesn’t grow on trees. But if you take this idea too far it’s probably less about being frugal and more about succumbing to <a href="https://www.npr.org/transcripts/598119170">scarcity mindset</a> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>Andy FryI don't have a single user yet and already I'm worried about saving money on hosting?What I learned launching my first product, part 22021-05-21T00:00:00+00:002021-05-21T00:00:00+00:00https://startupinamonth.net/what-i-learned-pt-2<p><a href="/what-i-learned-pt-1/">Read part 1 of this series</a>
<br />
<a href="/what-i-learned-pt-3/">Read part 3 of this series</a></p>
<p>Welcome! This post is part of a blog series about things that I’ve learned from releasing my first product as a bootstrapped startup founder. It’s been a massively educational process for me, and I want to share my learnings for the benefit of others and to document the journey for myself.</p>
<p>These posts are all about <a href="https://picstory.studio">PicStory</a>, which is an app that lets you make cool photo stories and share them with anybody. It has three goals:</p>
<ul>
<li>
<p>to give you more creative freedom to create than Instgram or Google Photos,</p>
</li>
<li>
<p>to give you a simple and accessible interface to create stories, and finally</p>
</li>
<li>
<p>to make it easier to share them with friends and family by not locking your content into a walled garden where you have to have an account to view a story.</p>
</li>
</ul>
<p>Check it out! <a href="https://app.picstory.studio/#/feedback">And let me know what you think</a>.</p>
<p><img src="/assets/images/what-i-learned/picstory-logo.png" alt="Pic Story Logo" /></p>
<p>So, let’s dive into lesson two:</p>
<h2 id="the-hunt-for-alice-or-the-tyranny-of-small-things">The Hunt for Alice, or: The Tyranny of Small Things</h2>
<p>As I got ready to put PicStory online, I wanted to do some final testing: a walkthrough of the whole flow of the app from start to finish to make sure that everything was working as it should. Logging in, making a first story, uploading photos, adding text, styling individual layout blocks: absolutely everything.</p>
<p>Much to my dismay, once I actually sat down to go through all of these “real world” user interaction flows, I found that hardly anything was working!</p>
<p>This came down, ironically, to the tech stack I had chosen to build the app, which, even more ironically, I had very intentionally chosen to give myself a productivity boost! And yet here I was, spending hours rewriting and tweaking a dozen little things. I built the whole app top to bottom in <a href="https://en.wikipedia.org/wiki/Clojure">Clojure</a> and <a href="https://clojurescript.org/">Clojurescript</a>. Without getting too deep into the tech aspects, the specific productivity gains I wanted from Clojure comes down to the isolated coding style it encourages. Clojure encourages you to write your functions in a “pure” way, and discourages interlinking your functions too much with the other parts of your app.</p>
<p>Not only that, it gives you a wonderful interactive environment for testing functions too: once you’ve written a function, you can start testing it instantly with demo code directly in the module in which you wrote it, which makes it easy to rapidly test and debug everything in isolation from the rest of your app.</p>
<p>One of the truly ironic consequences of this style of development is that if you’ve written a function in “isolation” from the rest of your app, then it’s very easy to write about 90% of a feature and then forget about it until you really need it. Many times I said to myself, “Ok, I’ve gotten this thing mostly figured out by now. It’s not quite ready to hook up to the rest of the app just yet, but hey, how hard can it be? It works in this little sandbox environment, all I need to do is just replace all of my stub code with the real thing and then I’ll be golden!”</p>
<p>In practice, this had two implications that I didn’t fully appreciate until it was go time:</p>
<ol>
<li>I thought I had “finished” much more of the app that I thought I had finished. Which is to say, I really had finished most of it, but a lot of it was all hard coded for a single test user: <a href="https://en.wikipedia.org/wiki/Alice_and_Bob">Alice</a>. For Alice, the app worked flawlessly. She could sign up, she could upload a photo, she could save a new story, it was fantastic! However, when it came time to test the app for other users like <a href="https://en.wikipedia.org/wiki/Alice_and_Bob">Bob</a>, it was a disaster. When I tried to sign up for the site, Alice greeted me as the logged in user on the home screen. When I uploaded a new photo for a story, it ended up in Alice’s folder on the server. When I saved one of Bob’s stories to edit for later, it never showed up in the list of stories for Bob. Guess where it did show up? <strong>In Alice’s account!</strong> I must have stubbed my toe on a thousand hidden Alices before I could truly say that the whole app could function 100% correctly for any user.</li>
<li>For old functions that I had written weeks ago, sometimes the rest of the codebase had changed to the point where those old functions no longer worked. It was never anything too drastic – an API endpoint which had changed names, a folder which had moved, a module with a new path – but each time I discovered something like this, it was just another vampire bat sucking out my momentum. It was taxing on my motivation: I was supposed to be in the end-game, and yet there I was retooling problems I thought I already had solved. It was mentally draining too. I was trying to juggle a hundred little tasks across all kinds of domains, and now I have to revisit a bunch of code I’ve already written, get my mind back into the context of what that old code is doing, perform the fix, test the fix, and reintegrate it into the app. It was rarely a big fix, but many small fixes quickly accumulate into a huge time sink.</li>
</ol>
<p>If I had written the app in a different language, I don’t think I would have had so many of those Alice problems. When your code is enmeshed and interlinked into one nasty hydra of an application, one broken piece can bring the whole thing screeching to a halt, at which point you have to go back and fix that one little piece no matter what it is. When your code exists as a bunch of small independent modules, those small breakages can be harder to notice, and your functions can moulder and become stale.</p>
<p>Granted, I would <em>still</em> write the whole thing in Clojure. The productivity gains from the language vastly outweigh these little problems that I encountered along the way. But in the future I’m going to be more careful to integrate a function or module into the app straight away to minimize these 11th hour surprises.</p>
<h2 id="one-last-thing">One last thing</h2>
<p>Thanks for reading this! It’s a busy world, and it means a lot that you spent some of your your time and attention on what I’m doing.</p>
<p>Would you like to join my mailing list? I send out updates once a month, so you can stay informed on what’s going on, but not <em>too</em> informed 😊</p>
<form action="https://sendy.startupinamonth.net/subscribe" method="POST" accept-charset="utf-8">
<label for="name">Name</label><br />
<input style="background: white;" type="text" name="name" id="name" />
<br />
<label for="email">Email</label><br />
<input style="background: white;" type="email" name="email" id="email" /><br /><br />
<div style="margin-bottom: 1.5rem;">
<span class="gdpr-permission">
<input type="checkbox" name="gdpr" id="gdpr" />
<label for="gdpr"><strong>Marketing permission</strong>:</label>
</span>
<span>
I give my consent to contact me via email for the purpose of news, updates and marketing. If you wish to withdraw your consent, click the unsubscribe link at the bottom of our emails or send an email to <a href="mailto:andy@startupinamonth.net">andy@startupinamonth.net</a>.
</span>
</div>
<br /><br />
<div style="display:none;">
<label for="hp">HP</label><br />
<input type="text" name="hp" id="hp" />
</div>
<input type="hidden" name="list" value="8xpdlVrcgW8921MAwkzOi1vw" />
<input type="hidden" name="subform" value="yes" />
<div>
<button class="primary-cta" type="submit" name="submit" id="submit">Sign me up!</button>
</div>
</form>Andy FryThe Hunt for Alice, or: The Tyranny of Small ThingsWhat I learned launching my first product, part 12021-05-07T00:00:00+00:002021-05-07T00:00:00+00:00https://startupinamonth.net/what-i-learned-pt-1<p><a href="/what-i-learned-pt-2/">Read part 2 of this series</a></p>
<p>Welcome! This post is part of a blog series about things that I’ve learned from releasing my first product as a bootstrapped startup founder. It’s been a massively educational process for me, and I want to share my learnings for the benefit of others and to document the journey for myself.</p>
<p>These posts are all about <a href="https://picstory.studio">PicStory</a>, which is an app that lets you make cool photo stories and share them with anybody. It has three goals:</p>
<ul>
<li>
<p>to give you more creative freedom to create than Instgram or Google Photos,</p>
</li>
<li>
<p>to give you a simple and accessible interface to create stories, and finally</p>
</li>
<li>
<p>to make it easier to share them with friends and family by not locking your content into a walled garden where you have to have an account to view a story.</p>
</li>
</ul>
<p>Check it out! <a href="https://app.picstory.studio/#/feedback">And let me know what you think</a>.</p>
<p><img src="/assets/images/what-i-learned/picstory-logo.png" alt="Pic Story Logo" /></p>
<p>So, let’s dive into lesson one:</p>
<h2 id="lesson-one-the-way-i-see-my-product-is-not-how-others-see-it">Lesson one: the way I see my product is not how others see it</h2>
<p>The first and hardest lesson of launching is that I haven’t delivered fully on my “vision” for Pic Story, or at least not yet. I spent so much time during the development process staring at this the individual leaves on the trees that I completely lost the big picture view of the forest, and the end result is this:</p>
<p>What I’ve built doesn’t make sense to anybody except for me!</p>
<p>This became evident to me while watching my wife (my very first user!) interact with the app for the first time. I watched as she went through the signup flow, initially thrilled at the prospect of seeing someone really use the thing. And then as she clicked around, my spirits started to dwindle.</p>
<ul>
<li>“Why would you click that button now?”</li>
<li>“Why did you click add photos? You haven’t even uploaded any photos yet!”</li>
<li>“You’re just leaving that toggled on? Can’t you see that you’re supposed to toggle it on and off?”</li>
</ul>
<p>Even still, I had tried intentionally to build the app out with that “first run” experience in mind<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. I had at least some clue that I needed to focus on teaching users how to use the app right away, or else I’d lose them instantly. When you sign into the app, the first thing you see is this:</p>
<p><img src="/assets/images/what-i-learned/picstory-landing.png" alt="Landing page for Pic Story after logging in" /></p>
<p>There it is, right at the top of the page: “New to PicStory? Start here.” This button takes you to a tutorial of the main features of the app. And even better, I built it using PicStory itself! This is a practice called <a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">dog fooding</a>, and I was particularly proud of it. After all, if I can’t build a compelling visual guide to PicStory WITH PicStory itself, have I really built something that others will be able to use to make cool, compelling stories of their own?</p>
<p>When you click that button, it takes you to the story editing UI itself:</p>
<p><img src="/assets/images/what-i-learned/story-edit.png" alt="Story editing UI" /></p>
<p>See the problem yet? I couldn’t either, until I actually saw other people using the app. NOBODY figured out that there is MORE content below bottom of the screen until I told them. You have to keep scrolling down to get to the meat of the tutorial:</p>
<p><img src="/assets/images/what-i-learned/story-edit-2.png" alt="Story editing UI below the fold" /></p>
<p>I blame a little bit of this on modern web browsers. Once upon a time the vertical scrollbar was prominent and always visible, so you always knew if there was more content to see below the fold. But regardless, this is the world we live in, and I need to adapt the app accordingly if I want to avoid this problem.</p>
<p>So there’s lesson one: I’m arguably the worst person to validate whether any of the UX or design aspects of the app are going to make sense to anybody, because on day one I’m already too deep into the inner workings of the app to appreciate how it will impress itself upon the naive user. I have firsthand knowledge of how the whole thing operates: an intimacy of understanding nobody else will ever have. If I inadvertently build in any funkiness into the UI, then it might skip my notice entirely, but it won’t skip the notice of others!</p>
<p>My advice to you if you’re trying to figure out whether to launch or whether to hold back: launch now! It doesn’t need to be a hard launch, the product doesn’t even need to be finished. By launching in any capacity, you’ll gain so much insight into your product by watching people use it. You might never gain this perspective otherwise, and understanding how others will see it is and use it is vital to your success. It will go on to inform future iterations of the product, and you’ll feel better knowing that you’re playing with a fuller deck of cards.</p>
<p>So just launch! You’ll be glad you did 😊</p>
<p>Check out <a href="/what-i-learned-pt-2/">part 2 of this series</a> for more lessons learned.</p>
<h2 id="one-last-thing">One last thing</h2>
<p>Thanks for reading this! It’s a busy world, and it means a lot that you spent some of your your time and attention on what I’m doing.</p>
<p>Would you like to join my mailing list? I send out updates once a month, so you can stay informed on what’s going on, but not <em>too</em> informed 😊</p>
<form action="https://sendy.startupinamonth.net/subscribe" method="POST" accept-charset="utf-8">
<label for="name">Name</label><br />
<input style="background: white;" type="text" name="name" id="name" />
<br />
<label for="email">Email</label><br />
<input style="background: white;" type="email" name="email" id="email" /><br /><br />
<div style="margin-bottom: 1.5rem;">
<span class="gdpr-permission">
<input type="checkbox" name="gdpr" id="gdpr" />
<label for="gdpr"><strong>Marketing permission</strong>:</label>
</span>
<span>
I give my consent to contact me via email for the purpose of news, updates and marketing. If you wish to withdraw your consent, click the unsubscribe link at the bottom of our emails or send an email to <a href="mailto:andy@startupinamonth.net">andy@startupinamonth.net</a>.
</span>
</div>
<br /><br />
<div style="display:none;">
<label for="hp">HP</label><br />
<input type="text" name="hp" id="hp" />
</div>
<input type="hidden" name="list" value="8xpdlVrcgW8921MAwkzOi1vw" />
<input type="hidden" name="subform" value="yes" />
<div>
<button class="primary-cta" type="submit" name="submit" id="submit">Sign me up!</button>
</div>
</form>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Patrick McKenzie talks about this in his talk about <a href="https://training.kalzumeus.com/first-run-experience">Improving the First Run Experience</a>. In short: if you don’t grab a user on their very first run-through with the app, then you’ve lost them. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>Andy FryJust launch! You'll be glad you did 😊05/06/2021 Livestream notes2021-05-06T00:00:00+00:002021-05-06T00:00:00+00:00https://startupinamonth.net/May-06-21-stream-notes<h2 id="mantra">Mantra</h2>
<p>(a small idea to guide the day’s work)</p>
<p>Small is good.</p>
<h2 id="plan">Plan</h2>
<p>(what do I plan to do)</p>
<p>Start writing some views for our DB contents!</p>
<ol>
<li>Step one: just get something in the frontend</li>
<li>Step two: start looking into sorting options (first and foremost date)</li>
</ol>
<h2 id="what-i-got-done">What I got done</h2>
<p>(plan != reality)</p>
<ul>
<li>Well … plan really didn’t match up with reality this time. I spent a big chunk of the stream trying to debug Shadow-CLJS + Caddy websocket issues. Tech infrastructure stuff like this feels like a huge time suck in the moment, but it’s important nevertheless to get it figured out, even if the time isn’t going directly towards feature development.</li>
<li>After that, we got re-posh + datascript integrated into the frontend! So far it’s just a copy/paste of the <a href="https://github.com/denistakeda/re-posh/tree/master/examples/todomvc">Todo MVC</a> example from the github repo, but it’ll be a great basis for building on top of.</li>
</ul>
<h2 id="for-next-time">For next time</h2>
<ul>
<li>Actually render something from the DB in the frontend.</li>
</ul>
<h2 id="links">Links</h2>
<p>(things referenced mid-stream)</p>
<ul>
<li><a href="https://startupinamonth.net/what-is-startup-in-a-month/">What is Startup in a Month?</a></li>
<li><a href="https://startupinamonth.net/mailfile-announcement/">What am I building this month?</a></li>
<li><a href="https://startupinamonth.net/discord/">Come hang out on Discord!</a></li>
<li><a href="https://jaymu.com/i-built-4-mini-products-in-3-days.html">Four projects in three days</a></li>
</ul>
<h2 id="stream-video">Stream Video</h2>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/9g1FDbJ10Rw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>Andy FryWell ... plan really didn't match up with reality this time.05/03/2021 Livestream notes2021-05-03T00:00:00+00:002021-05-03T00:00:00+00:00https://startupinamonth.net/May-03-21-stream-notes<h2 id="mantra">Mantra</h2>
<p>(a small idea to guide the day’s work)</p>
<p>Everything takes longer than you think it will.</p>
<h2 id="plan">Plan</h2>
<p>(what do I plan to do)</p>
<p>Now that we can get the relevant, plain text content of our emails without all the crazy RFC5322 stuff:</p>
<ul>
<li>Save the email to the DB
<ul>
<li>First real-world foray into Datomic 😳</li>
<li>Still need to figure out DB schema</li>
</ul>
</li>
<li>If we get that far, start building the frontend</li>
</ul>
<h2 id="what-i-got-done">What I got done</h2>
<p>(plan != reality)</p>
<ul>
<li>After some trials and tribulations, I finally figured out why my JSON was not showing up as JSON in the body of my POST requests: I needed a header in the request that specified that the request contains JSON 🙄</li>
<li>HOWEVER, we did complete the circle of sending an email, parsing it, and saving it in the backend! Huge step 👍</li>
<li>We also refactored from Reitit to Compojure: more docs, more examples, easier to work with altogether. And perhaps most significantly: I have a better feel for how to work with it than Reitit.</li>
<li>We made our first complex-ish Datomic write function: it saves one or many new entities in a DB, grabs their entity ids, and associates them with an existing user’s “saved-items” attribute. Pretty complicated stuff, but I’ll get more comfortable with it as I go along, and the query power of Datalog/the data model of Datomic will hopefully make up for the fact that writing to the DB can be a little bit complicated.</li>
</ul>
<h2 id="for-next-time">For next time</h2>
<ul>
<li>Start in on the frontend for the app: now that we can save content from our emails, we should work on viewing it next.</li>
</ul>
<h2 id="links">Links</h2>
<p>(things referenced mid-stream)</p>
<ul>
<li><a href="https://startupinamonth.net/what-is-startup-in-a-month/">What is Startup in a Month?</a></li>
<li><a href="https://startupinamonth.net/mailfile-announcement/">What am I building this month?</a></li>
<li><a href="https://startupinamonth.net/discord/">Come hang out on Discord!</a></li>
<li><a href="https://www.datomic.com/on-prem-documentation.html">Datomic on-prem docs: it’s not just an AWS database after all</a></li>
<li><a href="http://www.learndatalogtoday.org/">Learn Datalog Today: awesome resource for learning Datalog syntax</a></li>
</ul>
<h2 id="stream-video">Stream Video</h2>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/kPA_oAOc430" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>Andy FryWe made our first complex-ish Datomic write function!Discord invite2021-05-03T00:00:00+00:002021-05-03T00:00:00+00:00https://startupinamonth.net/discord<p>Want to come hang out on Discord? <a href="https://discord.gg/ZmCYckxKKG">Stop by and say hi!</a></p>Andy FryCome hang out with us!04/29/2021 Livestream notes2021-04-29T00:00:00+00:002021-04-29T00:00:00+00:00https://startupinamonth.net/Apr-29-21-stream-notes<h2 id="mantra">Mantra</h2>
<p>(a small idea to guide the day’s work)</p>
<p>Why so serious?</p>
<h2 id="plan">Plan</h2>
<p>(what do I plan to do)</p>
<ul>
<li>Figure out weird data stuff when POSTing email to backend server from AWS 🤷♂️</li>
<li>Once we can get this thing into a proper JSON object, we can then:
<ul>
<li>Parse the object</li>
<li>Get the email data (mainly subject and body)</li>
<li>Save the email in the DB</li>
<li>And then the core feature is more or less “done” (huge asterisk)</li>
</ul>
</li>
</ul>
<h2 id="what-i-got-done">What I got done</h2>
<p>(plan != reality)</p>
<ul>
<li>Found a library to parse RFC 5322 messages into Clojure maps!</li>
<li>Debugged our POST body woes: turns out Reitit uses a totally different middleware scheme than Compojure (big point in favor of “sticking with what you know” when it comes to your tools 😰)</li>
<li>ALMOST got to the point of grabbing subject and body of the email for storage in DB!</li>
</ul>
<h2 id="for-next-time">For next time</h2>
<ul>
<li></li>
</ul>
<h2 id="links">Links</h2>
<p>(things referenced mid-stream)</p>
<ul>
<li><a href="https://startupinamonth.net/what-is-startup-in-a-month/">What is Startup in a Month?</a></li>
<li><a href="https://startupinamonth.net/mailfile-announcement/">What is Mail File? Find out here!</a></li>
<li><a href="https://www.ietf.org/rfc/rfc5322.txt">RFC 5322 email message spec. We’ve struck internet bedrock ⛏</a></li>
</ul>
<h2 id="stream-video">Stream Video</h2>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/LmsHnASPzHE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>Andy Fry04/28/2021 Livestream notes2021-04-28T00:00:00+00:002021-04-28T00:00:00+00:00https://startupinamonth.net/Apr-28-stream-notes<h2 id="mantra">Mantra</h2>
<p>(a small idea to guide the day’s work)</p>
<p>Get back on the horse 🐎</p>
<h2 id="plan">Plan</h2>
<p>(what do I plan to do)</p>
<ul>
<li>Amazon SES is working! So we should figure out how to
<ul>
<li>Send emails (done this before, not hard 💪)</li>
<li>Receive emails (uh …🤔)</li>
</ul>
</li>
<li>If we can figure out how to receive emails, then …
<ul>
<li>Take our parsing/DB saving code from yesterday and hook that up to email receiving flow</li>
</ul>
</li>
</ul>
<h2 id="what-i-got-done">What I got done</h2>
<p>(plan != reality)</p>
<ul>
<li>We can now send emails from Mail File! Which is great.</li>
<li>We got most of the way towards <strong>receiving</strong> emails sent <strong>to</strong> Mail File as well:
<ul>
<li>We wrote an SES receipt rule which invokes an AWS Lambda function when an email is received</li>
<li>That AWS Lambda function sends a POST to the Mail File server which we’ve written to receive emails</li>
</ul>
</li>
</ul>
<p>Theoretically that would be enough: SES would send the subject and body of the email along in the data that it sends to Lambda, but evidently an intermediary step is required along the way: either invoking an SNS event or saving the email to S3 first. Seems like an unneccessary middle step to this guy, but that’s how it is 🤷♂️</p>
<h2 id="for-next-time">For next time</h2>
<ul>
<li>Set up either SNS or S3 as the middle step between SES and Lambda, and after that we will have our email contents and can parse/save them accordingly 🎉</li>
</ul>
<h2 id="links">Links</h2>
<p>(things referenced mid-stream)</p>
<ul>
<li><a href="https://arstechnica.com/information-technology/2014/02/how-to-run-your-own-e-mail-server-with-your-own-domain-part-1/">How to Run Your Own Email Server (and all the reasons why you DON’T want to do this too)</a></li>
<li><a href="https://theboroer.github.io/localtunnel-www/">LocalTunnel, used to make a publicly available URL for my localhost app. Expose yourself to the world!</a></li>
<li><a href="https://chrome.google.com/webstore/detail/restman/ihgpcfpkpmdcghlnaofdmjkoemnlijdi?hl=en">RESTMan: a Postman-like browser extension for making REST calls, v. useful</a></li>
<li><a href="https://stackoverflow.com/questions/38533580/nodejs-how-to-promisify-http-request-reject-got-called-two-times/38543075">Wrap Node.JS https module in a promise</a></li>
</ul>
<h2 id="stream-video">Stream Video</h2>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/XYTSPZVe5Vo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>Andy Fryafter that we will have our email contents and can parse/save them accordingly04/27/2021 Livestream notes2021-04-27T00:00:00+00:002021-04-27T00:00:00+00:00https://startupinamonth.net/Apr-27-21-stream-notes<h2 id="mantra">Mantra</h2>
<p>(a small idea to guide the day’s work)</p>
<p>The master has had more failures than the novice has even attempted.</p>
<h2 id="plan">Plan</h2>
<p>(what do I plan to do)</p>
<ul>
<li>Get back into building <a href="https://startupinamonth.net/mailfile-announcement/">Mail File</a></li>
<li>Get the DB up and running (Datomic)</li>
<li>Maybe start parsing dummy emails
<ul>
<li>Parse body of email by newline</li>
<li>Transact those parsed lines as individual entities to transact into DB</li>
</ul>
</li>
</ul>
<h2 id="what-i-got-done">What I got done</h2>
<p>(plan != reality)</p>
<ul>
<li>We finally got Datomic up and running! Turns out reading the documentation carefully can take you a long way in this world 👍</li>
<li>We added a route for saving a new URL from an email</li>
<li>We wrote:
<ul>
<li>specs for a “saved item” Datomic entity and a “link” Datomic entity</li>
<li>A function to save a new url</li>
</ul>
</li>
<li>And finally, we hooked up the API to submit a new URL via POST and save it in the DB!</li>
</ul>
<h2 id="for-next-time">For next time</h2>
<ul>
<li>Hopefully get AWS SES up and running so we can test these with actual emails</li>
<li>Expand functionality beyond just URLS, like quotes or other random pieces of text</li>
</ul>
<h2 id="links">Links</h2>
<p>(things referenced mid-stream)</p>
<ul>
<li><a href="www.learndatalogtoday.org/">Learn Datalog Today, excellent for learning Datalog syntax</a></li>
</ul>
<h2 id="stream-video">Stream Video</h2>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/hqwSim1fVx8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>Andy FryTurns out reading the documentation carefully can take you a long way in this world04/12/2021 Livestream notes2021-04-12T00:00:00+00:002021-04-12T00:00:00+00:00https://startupinamonth.net/Apr-12-21-stream-notes<h2 id="mantra">Mantra</h2>
<p>(a small idea to guide the day’s work)</p>
<p>I’ve learned a lot!</p>
<h2 id="plan">Plan</h2>
<p>(what do I plan to do)</p>
<ul>
<li>Introduce Mail File</li>
<li>Start the buildout</li>
</ul>
<h2 id="what-i-got-done">What I got done</h2>
<p>(plan != reality)</p>
<ul>
<li>Created the Mail File landing page!</li>
<li>And we got it to run locally via Vagrant!</li>
<li>We tried to put it online, but were foiled by SSH key shenaningans and config problems.</li>
</ul>
<h2 id="for-next-time">For next time</h2>
<ul>
<li>Get the Mail File landing page up and running</li>
<li>Work on Amazon SES config for emailing content to the service!</li>
</ul>
<h2 id="links">Links</h2>
<p>(things referenced mid-stream)</p>
<ul>
<li>PicStory, last month’s startup: <a href="https://picstory.studio/">https://picstory.studio/</a></li>
<li>Alfred, for keyboard shortcuts! and a lot of other stuff too: <a href="https://www.alfredapp.com">https://www.alfredapp.com</a></li>
<li>Jekyll for blogging: <a href="https://jekyllrb.com/">https://jekyllrb.com/</a></li>
</ul>
<h2 id="stream-video">Stream Video</h2>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/RE1BrD9sUnw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>Andy FryCreated the Mail File landing page!