<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Phil Freo</title><description>Phil Freo is a product person, programmer, and engineering leader. VP of Engineering &amp; Product at Close.</description><link>https://philfreo.com/</link><item><title>Typefully Profile</title><link>https://philfreo.com/blog/typefully-profile/</link><guid isPermaLink="false">https://philfreo.com/?p=954</guid><description>In recent years I’ve been doing less blogging here, but writing more on Twitter. When I have a bit more to say, I’ve found Typefully as a really nice writing tool. They also have…</description><pubDate>Fri, 03 Mar 2023 16:20:14 GMT</pubDate><content:encoded>&lt;p&gt;In recent years I’ve been doing less blogging here, but &lt;a href=&quot;https://twitter.com/philfreo&quot;&gt;writing more on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When I have a bit more to say, I’ve found &lt;a href=&quot;https://typefully.com/&quot;&gt;Typefully&lt;/a&gt; as a really nice writing tool. They also have a “Profile” view that rolls up tweet threads into blog post form. My latest posts are here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://typefully.com/philfreo&quot;&gt;&lt;strong&gt;https://typefully.com/philfreo&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
</content:encoded><category>Personal</category></item><item><title>Sabbaticals</title><link>https://philfreo.com/blog/sabbaticals/</link><guid isPermaLink="false">https://philfreo.com/?p=922</guid><description>I took my first sabbatical this year, after Close started (generously) offering fully paid 4-week sabbaticals for all employees after 5 years. Recently I spoke with our CEO…</description><pubDate>Fri, 13 Aug 2021 21:48:11 GMT</pubDate><content:encoded>&lt;p&gt;I took my first sabbatical this year, after Close started (generously) offering fully paid 4-week sabbaticals for all employees after 5 years.&lt;/p&gt;
&lt;p&gt;Recently I spoke with our CEO, &lt;a href=&quot;https://twitter.com/steli&quot;&gt;Steli&lt;/a&gt;, who just came back from his own sabbatical, about how our sabbaticals went and what we learned. Our conversation was recorded for our company’s internal podcast (we use &lt;a href=&quot;https://castos.com&quot;&gt;Castos&lt;/a&gt;) but I thought I’d also write up some of my sabbatical thoughts and lessons learned while our conversation is still fresh…&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-width=&quot;500&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;I’m on a 4-week sabbatical starting this week. Super grateful for &lt;a href=&quot;https://twitter.com/close?ref_src=twsrc%5Etfw&quot;&gt;@close&lt;/a&gt; offering this perk – I’m excited to be spending extra time with my wife and little kiddos, be able to recharge, catch-up on personal stuff etc. &lt;a href=&quot;https://t.co/mrEKV0y8Ud&quot;&gt;https://t.co/mrEKV0y8Ud&lt;/a&gt;&lt;/p&gt;— Phil Freo (@philfreo) &lt;a href=&quot;https://twitter.com/philfreo/status/1357127503459069954?ref_src=twsrc%5Etfw&quot;&gt;February 4, 2021&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;h2 id=&quot;why-take-a-sabbatical&quot;&gt;Why take a sabbatical?&lt;/h2&gt;
&lt;p&gt;Besides just being a great perk to take advantage of, a sabbatical is meant to be &lt;strong&gt;a time for rest, recharge, and renewal.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I’ve been at Close a very long time (9+ years). Given enough years in the same environment, anyone can get burnt out or just want to take an extended break. Vacations and other regular time off (among other strategies) can help avoid this, however a week at a beach (knowing that your inbox is piling up while you’re gone), is pretty different than taking a more extended break away from work.&lt;/p&gt;
&lt;p&gt;Both at Close and elsewhere, I’ve seen long-tenured employees quit or come close to quitting their job even without any specific plan on what they want to pursue next – simply because they felt burnt out or wanted unstructured time to reflect, deal with personal issues, and/or consider their next steps in life.&lt;/p&gt;
&lt;p&gt;A sabbatical is a great way to have space for this, without having to quit or use up a year’s worth of vacation time all at once.&lt;/p&gt;
&lt;p&gt;I think given several years in the same company, everyone would benefit from an extended break. Hopefully you come back refreshed and renewed!&lt;/p&gt;
&lt;h2 id=&quot;planning-a-sabbatical&quot;&gt;Planning a sabbatical&lt;/h2&gt;
&lt;h3 id=&quot;timing--when-should-i-schedule-my-sabbatical&quot;&gt;Timing – When should I schedule my sabbatical?&lt;/h3&gt;
&lt;p&gt;Sabbaticals are best planned months in advance, to give yourself and your team adequate time to prepare for your absence. Beyond having advance notice, it’s also good to choose a time when (as much as you can foresee) things aren’t going to be too hectic, so you can truly disconnect during it.&lt;/p&gt;
&lt;p&gt;I had first planned, in Feb 2020, to take my sabbatical in the fall of 2020. Not long after making those plans, COVID-19 introduced a huge amount of uncertainty into the world. It soon became pretty clear that 2020 was &lt;em&gt;not&lt;/em&gt; going to be a great time to leave the company in great shape since it was such a chaotic time, nor would it have been good for me to be stuck at home on sabbatical. So I waited until things were returning to normal and the company was in a more certain state before I ended up taking mine, earlier this year.&lt;/p&gt;
&lt;p&gt;That said, &lt;em&gt;there’s never going to be a perfect time to be away from work, so at some point you just need to pick dates and commit.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;expectations-hopes-and-goals&quot;&gt;Expectations, Hopes, and Goals&lt;/h3&gt;
&lt;p&gt;I had big expectations for my sabbatical. Beyond just wanting to take an extended break from work and recharge, I wanted to do a lot personally.&lt;/p&gt;
&lt;p&gt;I wanted to spend a bunch of extra quality time with my family. We also wanted to take a vacation and take our kids to see snow for the first time.&lt;/p&gt;
&lt;p&gt;In the several months prior, I had also accumulated a very large list of other things I wanted or needed to do. My Apple Reminders was full of tasks to accomplish, things to read/explore, home improvement projects, side project ideas, etc. I was excited to have time to really take a crack at this list.&lt;/p&gt;
&lt;p&gt;Twice previously, I had taken 4 weeks off for paternity leave and had looked forward to those for similar reasons. &lt;em&gt;But parental leave is very very different from a sabbatical&lt;/em&gt; – more about surviving, adjusting as a larger family, and helping with the baby/kids – pretty much the opposite of rest &amp;amp; renewal or lots of time for of personal interests.&lt;/p&gt;
&lt;h3 id=&quot;preparing-at-work&quot;&gt;Preparing at work&lt;/h3&gt;
&lt;p&gt;I lead Engineering at Close – nearly 20 people. It was critical that I left the team and company in good shape while I was on sabbatical so things could keep humming along without any disruption.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;4+ week sabbaticals are pretty different than a 1-2 week vacation&lt;/em&gt;. If you go away for a week or so, things generally operate fine without you. For example you may only miss one instance of a weekly recurring meeting. People will still email you and just expect that you catch up on everything in a few days. At 4+ weeks away, it’s different. Initiatives don’t (or at least shouldn’t) really pause while you’re gone, and you can’t just catch up on everything when you get back. You need to ensure your team can really operate without you, versus just waiting on your turn.&lt;/p&gt;
&lt;p&gt;So in the weeks leading up to my 4-week sabbatical, I worked hard to help the company be best prepared for my absence. Examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I helped ensure that our product development roadmap/plan was planned out a bit farther than was typical.&lt;/li&gt;
&lt;li&gt;I wrote up new internal wiki pages to document processes that I had previously been doing that I wanted to start delegating.
&lt;ul&gt;
&lt;li&gt;Example: We introduced a new “Cycle Lead” role that an engineering manager would take on to be the person most responsible for a specific 6-week cycle of product development work and communicating that work to the company.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Previously I had led all of our weekly Engineering Sync meetings. I changed that by setting a schedule with our engineering leadership team to rotate through leading these meetings both while I was out and after.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to allowing the team to operate smoothly while I was on sabbatical, these were all good things to do &lt;em&gt;anyway&lt;/em&gt;, and also gave a chance for others on the team to lead in new ways.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Writing more things down and having processes that don’t rely on you – these are all good things even if you’re not going on a sabbatical&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This preparation work allowed me to more confidently take time off without worrying about how things were going at work.&lt;/p&gt;
&lt;h2 id=&quot;the-time-off&quot;&gt;The Time Off&lt;/h2&gt;
&lt;p&gt;Finally, my sabbatical came! Now to enjoy it…&lt;/p&gt;
&lt;h3 id=&quot;keeping-a-log&quot;&gt;Keeping a log&lt;/h3&gt;
&lt;p&gt;First, I have one big tip for anyone taking extended time off…&lt;/p&gt;
&lt;p&gt;I kept a daily log of what I “worked on” or spent significant time on that day. I just used an Apple Note and just wrote a few bullets grouped per day. A day might just look like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gym&lt;/li&gt;
&lt;li&gt;Tax prep &amp;amp; call with CPA&lt;/li&gt;
&lt;li&gt;Lunch out + bike ride with family&lt;/li&gt;
&lt;li&gt;Caught up with Matt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This simple task helped with a few things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Help have an idea of where your time went&lt;/strong&gt;. It’s not hard to wonder “where has all my time been going?” after having long periods of unstructured time, and this helps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Give a sense of accomplishment&lt;/strong&gt; each time I added a new thing, even if that thing was just “fun” and not a task.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prepare for others’ questions&lt;/strong&gt;. Having a log helped me be able to look back over my entire sabbatical to be able to answer the common question every one of your colleagues will ask: “So, how was your sabbatical? What did you do?”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Don’t underestimate that last point… you can safely expect that pretty much every coworker you talk to in the following month will ask you what you did on your sabbatical. Especially if they haven’t had a chance to do a sabbatical yet, they may have big expectations for you! Be ready to give a clear answer. Reviewing a log can help.&lt;/p&gt;
&lt;h3 id=&quot;so-what-did-i-actually-do&quot;&gt;So what did I actually do?&lt;/h3&gt;
&lt;p&gt;Looking over my daily logs, my time went toward the following.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Family:&lt;/strong&gt; More quality time with my own family every day. Met up with extended family we don’t see often. And when a family member was in the hospital, I was able to visit them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vacation:&lt;/strong&gt; Planned a family trip to North Carolina and had a great time: road tripping, mountain cabin, snow tubing, alpine coaster, met up with friends there.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;House projects:&lt;/strong&gt; Organized garage, improved home audio setup, planned our house to be painted, got some trees trimmed, got some pool staining handled, fixed that pesky loose toilet paper holder, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Friends&lt;/strong&gt;: Caught up with quite a few friends that I hadn’t talked to in a long time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Personal finance:&lt;/strong&gt; Worked with a financial advisor, did a lot of reading about investing (&lt;a href=&quot;https://www.bogleheads.org/wiki/Main_Page&quot;&gt;Bogleheads&lt;/a&gt;, books, articles, &lt;a href=&quot;https://www.reddit.com/r/financialindependence/&quot;&gt;FI/RE&lt;/a&gt;), did an EOY finance &amp;amp; spending review, did tax prep / worked with CPA, restructured some charitable giving, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learning:&lt;/strong&gt; Read a couple eng management articles, learned about some new tech stuff, listened to podcasts, watched some talks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Networking:&lt;/strong&gt; Met with some local SaaS/tech people in my city.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;COVID vaccines:&lt;/strong&gt; Got my parents appointments for shots, my wife and I got our shots (made calls daily looking for leftover doses, etc.) Felt a big relief after getting this “project” done!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tech projects:&lt;/strong&gt; Improved my Raspberry Pi setup and a bit of work on other side projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shopping&lt;/strong&gt;: research and buying a new kitchen dining table set, a new printer, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In summary, my time was spent on a combination of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Family/fun/relaxation/downtime&lt;/li&gt;
&lt;li&gt;Work on my personal todo list&lt;/li&gt;
&lt;li&gt;Entertaining whatever unexpected things (good or bad) that came up&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;reflection&quot;&gt;Reflection&lt;/h2&gt;
&lt;p&gt;4 weeks off sounds like a lot of time, but I didn’t get to do probably even half of what I wanted to do. For example, I thought I’d make a lot of improvements to &lt;a href=&quot;https://philfreo.com/blog/introducing-teamhome/&quot;&gt;a side project&lt;/a&gt;, but didn’t end up spending any time on it at all.&lt;/p&gt;
&lt;p&gt;But I am happy with how I spent my time. In particular, I realized that I ended up spending a lot of time on good things I hadn’t planned on.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In hindsight, it was great having a combination of proactive stuff I wanted to do, but equally great to just have time and space for entertaining other unexpected things that come up&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I went back to work recharged and happy to be back. Extended time away gave me a fresh perspective and clarity on what was most important. &lt;em&gt;I went back with renewed focus on how I should best spend my time/energy at work.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;tips--lessons-learned&quot;&gt;Tips &amp;amp; Lessons Learned&lt;/h3&gt;
&lt;p&gt;Beyond learning a bunch through reading &lt;em&gt;during&lt;/em&gt; my sabbatical, here are my lessons learned &lt;em&gt;about&lt;/em&gt; taking sabbaticals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Anything at work I’d need to document or turn into a “process” in order to take extended time off – these are things I probably should have done regardless.&lt;/li&gt;
&lt;li&gt;It was really great to have a combination of
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;proactive&lt;/em&gt; plans and goals to move forward, plus&lt;/li&gt;
&lt;li&gt;&lt;em&gt;unstructured time&lt;/em&gt; to allow space to react to new opportunities and do things I didn’t plan on.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Keeping a log of where my time went was really helpful.&lt;/li&gt;
&lt;li&gt;What I’d do different next time:
&lt;ul&gt;
&lt;li&gt;Would have planned my actual travel plans well ahead of time, before things got mostly booked up.&lt;/li&gt;
&lt;li&gt;I’d have a bit lower expectation on how much I’d be able to do / how long the time would feel (it did go by very fast)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sabbaticals are a great way to clear your head and come back to work with clear perspective and priority on what’s important.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall my sabbatical was a great experience and I was really grateful for opportunity.&lt;/p&gt;
</content:encoded><category>Leadership</category><category>Personal</category></item><item><title>Introducing TeamHome</title><link>https://philfreo.com/blog/introducing-teamhome/</link><guid isPermaLink="false">https://philfreo.com/?p=903</guid><description>I built a micro-SaaS as my pandemic side project: TeamHome.app – An internal company directory designed for remote teams. Try it out and let me know what you think. Read on for…</description><pubDate>Tue, 29 Dec 2020 02:50:55 GMT</pubDate><content:encoded>&lt;p&gt;I built a micro-SaaS as my pandemic side project:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://teamhome.app&quot;&gt;TeamHome.app&lt;/a&gt; – An internal company directory designed for remote teams. Try it out and let me know what you think.&lt;/p&gt;
&lt;p&gt;Read on for details &amp;amp; some background…&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Tweetstorm version here:&lt;/em&gt;&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-width=&quot;500&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;🚀 I built a micro-SaaS as my pandemic side project:&lt;a href=&quot;https://t.co/sWnZ4ARwGt&quot;&gt;https://t.co/sWnZ4ARwGt&lt;/a&gt; – An internal company directory designed for remote teams.&lt;br&gt;&lt;br&gt;Try it out and let me know what you think.&lt;br&gt;&lt;br&gt;👇 Read on for details &amp;amp; behind-the-scenes…&lt;/p&gt;— Phil Freo (@philfreo) &lt;a href=&quot;https://twitter.com/philfreo/status/1341834952648912897?ref_src=twsrc%5Etfw&quot;&gt;December 23, 2020&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;Some screenshots:&lt;/p&gt;
&lt;div class=&quot;gallery&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://philfreo.com/wp-content/uploads/2021/01/profile-copy-2.png&quot;&gt;&lt;img alt=&quot;TeamHome profile page&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2557&quot; height=&quot;2138&quot; src=&quot;https://philfreo.com/_astro/profile-copy-2.9k-SMwAd_2op3Lt.webp&quot; srcset=&quot;https://philfreo.com/_astro/profile-copy-2.9k-SMwAd_Z1n2Rux.webp 640w, https://philfreo.com/_astro/profile-copy-2.9k-SMwAd_Zv4WeX.webp 750w, https://philfreo.com/_astro/profile-copy-2.9k-SMwAd_Z1vNtMo.webp 828w, https://philfreo.com/_astro/profile-copy-2.9k-SMwAd_Z1vz3WA.webp 1080w, https://philfreo.com/_astro/profile-copy-2.9k-SMwAd_2gKo8a.webp 1280w, https://philfreo.com/_astro/profile-copy-2.9k-SMwAd_1pLqwn.webp 1668w, https://philfreo.com/_astro/profile-copy-2.9k-SMwAd_KUCDd.webp 2048w, https://philfreo.com/_astro/profile-copy-2.9k-SMwAd_2op3Lt.webp 2557w&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://philfreo.com/wp-content/uploads/2021/01/fields-copy.png&quot;&gt;&lt;img alt=&quot;TeamHome custom profile fields&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2162&quot; height=&quot;1916&quot; src=&quot;https://philfreo.com/_astro/fields-copy.DtKQkl4V_Z1AeRyN.webp&quot; srcset=&quot;https://philfreo.com/_astro/fields-copy.DtKQkl4V_Z1ersXy.webp 640w, https://philfreo.com/_astro/fields-copy.DtKQkl4V_Z1Tn7g.webp 750w, https://philfreo.com/_astro/fields-copy.DtKQkl4V_2qzAvh.webp 828w, https://philfreo.com/_astro/fields-copy.DtKQkl4V_Z1oMxHE.webp 1080w, https://philfreo.com/_astro/fields-copy.DtKQkl4V_Z1AlA4r.webp 1280w, https://philfreo.com/_astro/fields-copy.DtKQkl4V_Z1MDya8.webp 1668w, https://philfreo.com/_astro/fields-copy.DtKQkl4V_Z1vknbI.webp 2048w, https://philfreo.com/_astro/fields-copy.DtKQkl4V_Z1AeRyN.webp 2162w&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://philfreo.com/wp-content/uploads/2021/01/times-cropped-1.png&quot;&gt;&lt;img alt=&quot;TeamHome team time zones&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1100&quot; height=&quot;862&quot; src=&quot;https://philfreo.com/_astro/times-cropped-1.hf5NhkvW_2mrnhh.webp&quot; srcset=&quot;https://philfreo.com/_astro/times-cropped-1.hf5NhkvW_1gychL.webp 640w, https://philfreo.com/_astro/times-cropped-1.hf5NhkvW_ZQTEWi.webp 750w, https://philfreo.com/_astro/times-cropped-1.hf5NhkvW_1dQm3k.webp 828w, https://philfreo.com/_astro/times-cropped-1.hf5NhkvW_1uIYW0.webp 1080w, https://philfreo.com/_astro/times-cropped-1.hf5NhkvW_2mrnhh.webp 1100w&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://philfreo.com/wp-content/uploads/2021/01/map-cropped.png&quot;&gt;&lt;img alt=&quot;TeamHome world map of the team&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2466&quot; height=&quot;1527&quot; src=&quot;https://philfreo.com/_astro/map-cropped.DldvGeHP_oKrPD.webp&quot; srcset=&quot;https://philfreo.com/_astro/map-cropped.DldvGeHP_ZnyjYs.webp 640w, https://philfreo.com/_astro/map-cropped.DldvGeHP_FaQjQ.webp 750w, https://philfreo.com/_astro/map-cropped.DldvGeHP_ZkYQVS.webp 828w, https://philfreo.com/_astro/map-cropped.DldvGeHP_1k6a4D.webp 1080w, https://philfreo.com/_astro/map-cropped.DldvGeHP_ZoIetn.webp 1280w, https://philfreo.com/_astro/map-cropped.DldvGeHP_1adQVV.webp 1668w, https://philfreo.com/_astro/map-cropped.DldvGeHP_ZTs0tH.webp 2048w, https://philfreo.com/_astro/map-cropped.DldvGeHP_oKrPD.webp 2466w&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I built &lt;a href=&quot;https://teamhome.app/&quot;&gt;TeamHome&lt;/a&gt; to both:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;give me what I always wished existed for seeing my remote team&lt;/li&gt;
&lt;li&gt;productize some ideas I think have helped &lt;a href=&quot;https://close.com/&quot;&gt;Close&lt;/a&gt; as we’ve grown from 6 to 40+ people remotely.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It has 2 modes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Just you&lt;/strong&gt; – visualize your coworkers’ time zones, see a world map of your company, &amp;amp; embed to keep your public About/Team page always up-to-date.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Team&lt;/strong&gt; – when embraced company wide, everyone has rich profiles/bios to serve as an official company directory.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Big companies get entire teams dedicated to building internal tools.&lt;/p&gt;
&lt;p&gt;I wanted to help smaller companies… to make smooth what’s commonly a duck tape solution where companies keep their team information together across wikis, spreadsheets, manual emails, google docs, etc.&lt;/p&gt;
&lt;p&gt;It’s been years since I’ve created a product from scratch, and I was getting the itch to make something new. I wanted to try out some new frameworks and just experiment some. Side projects are great for that.&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-width=&quot;500&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Been working on a side project (hope to launch soon) and it&apos;s been a good reminder – side projects are *so good* for learning new technologies, experimenting with new ideas, playing with new SaaS services, and just building something in a quick and low-stakes way.&lt;/p&gt;— Phil Freo (@philfreo) &lt;a href=&quot;https://twitter.com/philfreo/status/1329790698531532800?ref_src=twsrc%5Etfw&quot;&gt;November 20, 2020&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;There’s basically nothing brand new here, just packaging together a few different ideas in a hopefully useful way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stripe.com/blog/stripe-home&quot;&gt;Stripe Home&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/how-you-revolutionize-way-your-team-works-together-all-david-politis/&quot;&gt;User Manuals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Mapbox maps, Slack API, Tailwind UI, etc. (details further on)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was just a little side project for me… why did I bother?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To see something like this in the world&lt;/li&gt;
&lt;li&gt;To experiment w/new tech &amp;amp; building from scratch&lt;/li&gt;
&lt;li&gt;To stay sane during a pandemic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Plus, I’ve seen how useful User Manuals can be, especially when new hires join&lt;/p&gt;
&lt;p&gt;Give it a shot – you can use it for free and see your team as soon as you sign in with Slack. I’d really love any feedback!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://teamhome.app/&quot;&gt;TeamHome.app&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;behind-the-scenes&quot;&gt;Behind-the-scenes&lt;/h3&gt;
&lt;p&gt;Building a SaaS like this has never been easier. I got some questions on what it’s built with.&lt;/p&gt;
&lt;p&gt;So I wanted to give some shout outs to what made building &lt;a href=&quot;https://teamhome.app&quot;&gt;TeamHome&lt;/a&gt; in a very limited amount of time possible… 🙌&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;App UI: &lt;a href=&quot;https://tailwindui.com/&quot;&gt;Tailwind UI&lt;/a&gt; components made it easy to quickly create something that feels rather polished.&lt;/li&gt;
&lt;li&gt;CSS: &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind&lt;/a&gt;, which I really started liking and would choose for future projects.&lt;/li&gt;
&lt;li&gt;Icons: &lt;a href=&quot;https://heroicons.com/&quot;&gt;Heroicons.com&lt;/a&gt; pair nicely with the above&lt;/li&gt;
&lt;li&gt;JavaScript: &lt;a href=&quot;https://stimulus.hotwire.dev/&quot;&gt;Stimulus&lt;/a&gt; – paired nicely with Rails to give my JS some structure. For any site that isn’t a SPA but needs some JS, this is a really great choice.&lt;/li&gt;
&lt;li&gt;Backend: Ruby on Rails – such a rich ecosystem of gems meant I wasn’t reinventing the wheel.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A few more:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mapbox.com/&quot;&gt;Mapbox&lt;/a&gt; – Beautiful maps API&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://slack.com/&quot;&gt;Slack&lt;/a&gt; – Thanks for a rather open API that let’s you see your entire Team automatically&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://render.com/&quot;&gt;Render&lt;/a&gt; – A formidable Heroku competitor at lower cost&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stripe.com/&quot;&gt;Stripe&lt;/a&gt; – With Checkout &amp;amp; Customer Portal, I had to write almost no billing-related code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And lots of open source code. As I mentioned, mostly just glueing ideas together.&lt;/p&gt;
</content:encoded><category>CSS</category><category>Design</category><category>Personal</category><category>Product</category><category>Remote</category><category>SaaS</category><category>Startups</category><category>Web Development</category></item><item><title>Using Lambda@Edge to setup server-side redirects on a Cloudfront+S3 static site</title><link>https://philfreo.com/blog/using-lambdaedge-to-setup-server-side-redirects-on-a-cloudfronts3-static-site/</link><guid isPermaLink="false">https://philfreo.com/?p=875</guid><description>As an engineering team at @Close, we’re trying to write more about what we are learning/doing. I recently got to dabble in a little coding project, so I wrote it up. This post…</description><pubDate>Wed, 12 Feb 2020 16:48:06 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;As an engineering team at &lt;a href=&quot;https://twitter.com/close&quot;&gt;@Close&lt;/a&gt;, we’re trying to write more about what we are learning/doing. I recently got to dabble in a little coding project, so I wrote it up. &lt;a href=&quot;https://engineering.close.com/posts/redirects-using-cloudfront-lambda-edge&quot;&gt;This post originally appeared on the Close Engineering Blog here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We host our main marketing website, &lt;a href=&quot;https://close.com/&quot;&gt;close.com&lt;/a&gt;, as a static site (in our case, generated via &lt;a href=&quot;https://www.getlektor.com/&quot;&gt;Lektor&lt;/a&gt;) served by AWS. We store all the staticly generated files in S3 and have a Cloudfront distribution in front as our CDN.&lt;/p&gt;
&lt;p&gt;Occasionally, we’ll need to setup a redirect, to point an old URL to a new URL, for example when combining two pages.&lt;/p&gt;
&lt;p&gt;In S3 hosted static websites like ours, there were basically two main ways to accomplish redirects…&lt;/p&gt;
&lt;h2 id=&quot;approach-1-html-meta-tag-redirects&quot;&gt;Approach 1: HTML meta tag redirects&lt;/h2&gt;
&lt;p&gt;The first is to do HTML meta redirects, by putting this tag on the page to redirect away from:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;meta http-equiv=&quot;refresh&quot; content=&quot;0; url=http://close.com/new-url-here&quot; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whether your hand-code each redirect in this way, or use your static site generator to help (e.g. &lt;a href=&quot;https://www.getlektor.com/docs/guides/redirects/&quot;&gt;Lektor’s support for Redirects&lt;/a&gt;), the result is the same – redirects that happen fully client-side.&lt;/p&gt;
&lt;p&gt;While this approach is convenient since everything is 100% static, it can be difficult to maintain in a large website and has real downsides for both performance and SEO compared to server-side redirects.&lt;/p&gt;
&lt;h2 id=&quot;approach-2-s3-static-website-hosting-redirects&quot;&gt;Approach 2: S3 Static Website Hosting Redirects&lt;/h2&gt;
&lt;p&gt;S3 does have some &lt;a href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/dev/how-to-page-redirect.html&quot;&gt;support for server-side redirects&lt;/a&gt;, which we were using in addition to meta tags previously described.&lt;/p&gt;
&lt;p&gt;These redirects do solve the performance and SEO concerns compared to meta redirect tags.&lt;/p&gt;
&lt;p&gt;Here’s an example of that XML format (configured in AWS Console under your S3 bucket &amp;gt; Properties &amp;gt; Static website hosting &amp;gt; Redirection Rules):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;RoutingRules&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;RoutingRule&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;Condition&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;KeyPrefixEquals&amp;gt;home&amp;lt;/KeyPrefixEquals&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;/Condition&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;Redirect&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;Protocol&amp;gt;https&amp;lt;/Protocol&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;HostName&amp;gt;close.com&amp;lt;/HostName&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;ReplaceKeyWith/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;HttpRedirectCode&amp;gt;301&amp;lt;/HttpRedirectCode&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;/Redirect&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/RoutingRule&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;RoutingRule&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;Condition&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;KeyPrefixEquals&amp;gt;old-page&amp;lt;/KeyPrefixEquals&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;/Condition&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;Redirect&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;Protocol&amp;gt;https&amp;lt;/Protocol&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;HostName&amp;gt;close.com&amp;lt;/HostName&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;ReplaceKeyWith/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;HttpRedirectCode&amp;gt;302&amp;lt;/HttpRedirectCode&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;/Redirect&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/RoutingRule&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/RoutingRules&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Problems with this approach included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Their XML syntax is verbose&lt;/li&gt;
&lt;li&gt;Difficulty redirecting to URLs that contain querystrings (&lt;a href=&quot;https://tusharghate.com/amazon-s3-bucket-redirect-with-query-params&quot;&gt;details&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Desire for our marketing team to be able to setup webpage redirects without AWS access&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, it was time for a new approach.&lt;/p&gt;
&lt;h2 id=&quot;approach-3-using-lambdaedge-and-cloudfront-to-do-server-side-redirects&quot;&gt;Approach 3: Using Lambda@Edge and Cloudfront to do server-side redirects&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/lambda/edge/&quot;&gt;Lambda@Edge&lt;/a&gt; is a feature of Cloudfront that allows you to run serverless functions to tweak the HTTP requests or responses between Cloudfront and your Origin or visitor.&lt;/p&gt;
&lt;p&gt;We had recently deployed an &lt;a href=&quot;https://gist.github.com/philfreo/3497afb69c3fe737c523fe347d8a4309&quot;&gt;extremely simple Lambda@Edge function&lt;/a&gt; on our close.com Cloudfront distribution in order to force visitors to the HTTPS version of our site by adding the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security&quot;&gt;HSTS header&lt;/a&gt; to all responses.&lt;/p&gt;
&lt;p&gt;We could then build upon this to create a user-friendly system for managing redirects.&lt;/p&gt;
&lt;h3 id=&quot;goal&quot;&gt;Goal&lt;/h3&gt;
&lt;p&gt;I envisioned a very simple system like the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep a &lt;code&gt;redirects.json&lt;/code&gt; file in the same GitHub repository as rest of our website, which our marketing team could easily maintain (with some instructions nearby).&lt;/li&gt;
&lt;li&gt;Have a Lambda@Edge function attached to our Cloudfront distribution do server-side redirects based on this file.&lt;/li&gt;
&lt;li&gt;Setup our CI/CD pipeline (in CircleCI) so that changes to redirects.json would get deployed automatically.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s the redirects.json format I went with:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;/jobs&quot;: { &quot;to&quot;: &quot;https://jobs.close.com/&quot;, &quot;statusCode&quot;: 301 },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;/resources/the-follow-up-formula&quot;: { &quot;to&quot;: &quot;/resources/followup&quot;, &quot;statusCode&quot;: 301 },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ... etc. ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Just be careful that your last entry does not contain a trailing comma.)&lt;/p&gt;
&lt;h3 id=&quot;setting-up-the-lambdaedge-function&quot;&gt;Setting up the Lambda@Edge function&lt;/h3&gt;
&lt;p&gt;Here’s the cheatsheet for creating the function properly:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;From the &lt;a href=&quot;https://console.aws.amazon.com/lambda/home?region=us-east-1#/create/function&quot;&gt;Lambda section of the AWS console&lt;/a&gt;, create a new Lambda function and give it a name like &lt;code&gt;cloudfront-site-redirects&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Choose the Node.js 10.x runtime, because that’s currently the latest that Lambda@Edge supports (despite regular Lambda functions supporting Node.js 12.x).&lt;/li&gt;
&lt;li&gt;Under “Permissions”, choose “Create a new role from AWS policy templates” and search for “Basic Lambda@Edge permissions (for Cloudfront trigger)”, which will setup a good IAM role for you (do &lt;em&gt;not&lt;/em&gt; choose “Create a new role with basic Lambda permissions” which is insufficient).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then…&lt;/p&gt;
&lt;h4 id=&quot;add-code-for-the-lambda-function&quot;&gt;Add code for the Lambda function&lt;/h4&gt;
&lt;p&gt;Ours ended up looking something like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const REDIRECTS_DATA_REGION = &apos;us-east-1&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const REDIRECTS_DATA_BUCKET = &apos;example.com&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const REDIRECTS_DATA_OBJECT = &apos;config/redirects.json&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const AWS = require(&apos;aws-sdk&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const s3 = new AWS.S3({ region: REDIRECTS_DATA_REGION });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;async function readFile(bucketName, filename) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const params = { Bucket: bucketName, Key: filename };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const response = await s3.getObject(params, function (err, data) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if (err) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      console.error(&apos;s3.getObject error: &apos; + err);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }).promise();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return response.Body.toString();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;async function getRedirectsData() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const txt = await readFile(REDIRECTS_DATA_BUCKET, REDIRECTS_DATA_OBJECT);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let data;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  try {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    data = JSON.parse(txt);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } catch (e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.error(&apos;getRedirectsData parse failed&apos;, txt, e);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    data = {};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return data;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const supportedStatusCodes = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  301: &apos;Moved Permanently&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  302: &apos;Found&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  307: &apos;Temporary Redirect&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;exports.handler = async(event, context) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // Get the incoming request and the initial response from S3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const { request, response } = event.Records[0].cf;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // Enable HSTS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  response.headers[&apos;strict-transport-security&apos;] = [{ key: &apos;Strict-Transport-Security&apos;, value: &apos;max-age=63072000&apos; }];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // request.uri is just the URL path without hostname or querystring&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let path = request.uri&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // Cut off trailing slash to normalize it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if (path.slice(-1) === &apos;/&apos;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    path = path.slice(0, -1);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const redirectsData = await getRedirectsData();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const redirectData = redirectsData[path];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // See if a redirect exists &amp;amp; sanity check that the redirect object is what we expect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const shouldRedirect = !!(redirectData &amp;amp;&amp;amp; redirectData.to &amp;amp;&amp;amp; supportedStatusCodes[redirectData.statusCode]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if (shouldRedirect) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Note: We can&apos;t completely create a new set of headers even for a redirect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // because Cloudfront fails painfully if you set (or clear) a &quot;read-only&quot; header.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // For Origin Response triggers, that includes leaving &quot;Transfer-Encoding&quot; alone.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // https://github.com/awsdocs/amazon-cloudfront-developer-guide/blob/master/doc_source/lambda-requirements-limits.md#read-only-headers&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    response.headers[&apos;location&apos;] = [{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      key: &apos;Location&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      value: redirectData.to,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      status: redirectData.statusCode,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      statusDescription: supportedStatusCodes[redirectData.statusCode],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      headers: response.headers,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return response;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;give-the-iam-role-access-to-your-s3-bucket&quot;&gt;Give the IAM role access to your S3 bucket&lt;/h4&gt;
&lt;p&gt;On the Lambda page, scroll down to find “Execution role” which gives you a link to the IAM role that was auto-generated for you. Now we are going to expand this role by attaching an additional Policy so it also has permission to access the S3 bucket containing our &lt;code&gt;redirects.json&lt;/code&gt; file.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;Version&quot;: &quot;2012-10-17&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;Statement&quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            &quot;Effect&quot;: &quot;Allow&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            &quot;Action&quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                &quot;s3:getObject&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            &quot;Resource&quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                &quot;arn:aws:s3:::example.com&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                &quot;arn:aws:s3:::example.com/*&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your S3 bucket isn’t public, you may need to change its policy too.&lt;/p&gt;
&lt;h4 id=&quot;publish&quot;&gt;Publish&lt;/h4&gt;
&lt;p&gt;From your Lambda function’s page:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click “Save”&lt;/li&gt;
&lt;li&gt;Go to Actions &amp;gt; Deploy to Lambda@Edge&lt;/li&gt;
&lt;li&gt;Choose the Cloudfront distribution to attach this to, and set it up using &lt;strong&gt;Origin response&lt;/strong&gt; as the trigger.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We choose “Origin Response” because this allows Cloudfront to cache the redirect itself similar to how it would cache any other response from the Origin.&lt;/p&gt;
&lt;p&gt;Then, head to your Cloudfront distribution in AWS. You’ll notice its status is “InProgress” as the changes are propagated. You’ll need to wait for this, and you’ll also need to create an Invalidation (probably for &lt;code&gt;/*&lt;/code&gt;) and wait for that too. (This process can take several minutes or more. &lt;a href=&quot;https://twitter.com/philfreo/status/1207070622628634624&quot;&gt;Ugh&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Finally, if everything is working properly, you can test out your new server-side redirects!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ curl -I https://close.com/resources/the-follow-up-formula                &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;HTTP/2 301 &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;location: /resources/followup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;continuous-deployment&quot;&gt;Continuous deployment&lt;/h4&gt;
&lt;p&gt;Finally, just make sure that your regular deployment process uploads &lt;code&gt;redirects.json&lt;/code&gt; to your S3 bucket (if it’s not already) and that a Cloudfront invalidatoin is created. For us that meant having a few lines in our deployment script like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;aws s3 cp config/redirects.json s3://example.com/config/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;aws configure set preview.cloudfront true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;aws cloudfront create-invalidation --distribution-id THE_ID_HERE --paths &apos;/*&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Doing so has allowed our marketing team to have simple control over server-side redirects on our site.&lt;/p&gt;
&lt;h4 id=&quot;troubleshooting-and-other-learnings&quot;&gt;Troubleshooting and other learnings&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;As mentioned above, troubleshooting on real deployments can be a time-consuming process. Therefore, you can use a “Test Event” to simulate your code way faster than deploying and waiting for invalidation each time.&lt;/li&gt;
&lt;li&gt;To see if a Cloudfront distribution is using Lambda@Edge, go to the distribution &amp;gt; Behaviors &amp;gt; Edit and at the very bottom you’ll see the ARN with function name &amp;amp; version listed.&lt;/li&gt;
&lt;li&gt;When publishing a new Lambda version, you can leave the Name blank and it will autoincrement a version number.&lt;/li&gt;
&lt;li&gt;From your Lambda function, you can find a link to get to the CloudWatch Logs, which can be very helpful for debugging.&lt;/li&gt;
&lt;li&gt;In Cloudfront Logs: Look at latest Log Groups for both “LambdaEdge” as well as name of your function.&lt;/li&gt;
&lt;li&gt;You can only have 1 Lambda function deployed per trigger (e.g. for Origin Response) per distribution, meaning that the lambda function name should probably be specific to that distribution. (At first I thought I could add a second independent function on top of the existing HSTS one. That doesn’t work, however they do have a “Layers” feature I didn’t explore).&lt;/li&gt;
&lt;li&gt;Lambda functions can use &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; in Node 10.x, so it’s nice to use those despite most online examples using callbacks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Feel free to hit me up at &lt;a href=&quot;https://twitter.com/philfreo&quot;&gt;@philfreo&lt;/a&gt; with any comments or questions!&lt;/p&gt;
</content:encoded><category>AWS</category><category>Engineering</category></item><item><title>When, Who, How to Hire an Engineering Team (including Hiring Remotely)</title><link>https://philfreo.com/blog/when-who-how-to-hire-an-engineering-team-including-hiring-remotely/</link><guid isPermaLink="false">https://philfreo.com/?p=775</guid><description>This is the blog post version of a talk I gave at True University in Berkley, CA in June 2018 to help early stage startups scale their engineering teams from 2 to 20. Hiring is…</description><pubDate>Sat, 30 Jun 2018 20:21:32 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This is the blog post version of a talk I gave at &lt;a href=&quot;http://trueventures.com/true-university/&quot;&gt;True University&lt;/a&gt; in Berkley, CA in June 2018 to help early stage startups scale their engineering teams from 2 to 20.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Hiring is hard. Really hard. I’ve been hiring on small startup engineering teams for the past 8 years (most recently grown &lt;a href=&quot;https://close.com/&quot;&gt;Close&lt;/a&gt;’s engineering team from 2 to 14) and hiring really great engineers never gets easy. But it does get &lt;em&gt;easier&lt;/em&gt; with practice and the right strategy.&lt;/p&gt;
&lt;p&gt;Here I’m going to share some things I’ve learned about when and how to find the right people for your stage, how to structure a hiring process, and distributed/remote work. And most importantly: mistakes I’ve made along the way at Close.&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-width=&quot;500&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Hiring. Is. Hard.&lt;/p&gt;— Phil Freo (@philfreo) &lt;a href=&quot;https://twitter.com/philfreo/status/608884949710610433?ref_src=twsrc%5Etfw&quot;&gt;June 11, 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;h1 id=&quot;when-to-hire&quot;&gt;When to hire&lt;/h1&gt;
&lt;p&gt;There are just &lt;strong&gt;two simple rules&lt;/strong&gt; to follow about &lt;em&gt;when&lt;/em&gt; to start hiring your engineering team.&lt;/p&gt;
&lt;h2 id=&quot;dont-hire-too-early-rule-1&quot;&gt;Don’t hire too early (rule #1)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Hiring is hard.&lt;/strong&gt; It’s a REALLLLLLLY hard, slow, painful process. Seriously, you don’t want to go through it before you have to. Avoid the pain as long as you can!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;People are really expensive&lt;/strong&gt; ($$$). Generally nothing in your startup will cost more money or take up more of your time and attention than… people.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do the job yourself first&lt;/strong&gt;. Be busy. Learn and do all the things! If you do everything yourself for a while, you’ll be in better shape to understand what really needs doing. You’ll be able to write a better job description, and you’ll be better at identifying a good person to fill the role when the time comes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Small = fast &amp;amp; nimble.&lt;/strong&gt; As much as everyone tries to avoid it, there’s just always inherent communication and process overhead of having a larger team. Enjoy moving as fast as possible while you’re small.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Focus on what matters: building a product that people love.&lt;/strong&gt; Spending your time on hiring activities (job posts, resume screening, phone screens, in-person interviews, job offers, great employee onboarding, etc.) – none of these &lt;em&gt;directly&lt;/em&gt; matter to your business! What matters first is finding product-market fit with a product that people love. Customers paying you money.&lt;/p&gt;
&lt;p&gt;At Close, we got to company profitability making a few million dollars in annual recurring revenue, all with a 3 person Product/Engineering team (with total company size of 6). Take advantage of your small team size as long as you can!&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-width=&quot;500&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;At Close.io we got to a few million in ARR with a product/eng team of three. &lt;a href=&quot;https://t.co/OtCd0TjH70&quot;&gt;https://t.co/OtCd0TjH70&lt;/a&gt;&lt;/p&gt;— Phil Freo (@philfreo) &lt;a href=&quot;https://twitter.com/philfreo/status/892938578711519233?ref_src=twsrc%5Etfw&quot;&gt;August 3, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;h2 id=&quot;dont-hire-too-late-rule-2&quot;&gt;Don’t hire too late (rule #2)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Because it takes forever.&lt;/strong&gt; Remember how I said hiring is a really hard, slow, painful process? This means that if you wait too long to start hiring… by the time you really accomplish your hiring goals, it will take you even longer than you’d think to add more people to the team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Our mistake:&lt;/em&gt;&lt;/strong&gt; At Close.io, we started hiring way too late. We were heads down focused exclusively on building and supporting our product for the first few years. By the time we finally decided to start hiring, we were already in a bad spot. We had too small of a team to support our product’s complexity and all of our customers. We slowed down on our pace of product development. We just didn’t have the team size that the business size and opportunity size justified.&lt;/p&gt;
&lt;p&gt;To make matters worse, once we started recruiting/hiring, we didn’t really try hard enough at first. We failed at it for too long because we didn’t &lt;em&gt;really&lt;/em&gt; make it a true priority even after we started.&lt;/p&gt;
&lt;h2 id=&quot;so-when&quot;&gt;So, when?&lt;/h2&gt;
&lt;p&gt;I can’t tell you exactly when to hire, but…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Wait until you know you &lt;em&gt;really&lt;/em&gt; need to&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When your current processes are breaking&lt;/li&gt;
&lt;li&gt;When you know you can’t accomplish your goals without more people&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;But then don’t wait any longer&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Once it’s time, make it a #1 priority&lt;/li&gt;
&lt;li&gt;You must really commit to hiring.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When we finally did really put in a full effort to grow our engineering team, it started taking 50%+ of my time for many months. You can’t half do it and expect results.&lt;/p&gt;
&lt;h1 id=&quot;who-to-hire&quot;&gt;Who to hire&lt;/h1&gt;
&lt;p&gt;Just one simple rule here: &lt;strong&gt;Aim high. Don’t settle.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;aim-high&quot;&gt;Aim high&lt;/h2&gt;
&lt;p&gt;In your early engineers, you should look for:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Senior people.&lt;/strong&gt; Junior people can be a huge asset longterm but will take years to get there, you probably don’t have time for that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Leadership potential.&lt;/strong&gt; You need people you don’t need to “manage”. Your first few engineers should be people who you think you can trust leading major projects and taking on large amounts of responsibilities. They should be people you can envision leading future teams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;They should “wow” you&lt;/strong&gt; somewhere along the way. Can you learn from this person?&lt;/p&gt;
&lt;h2 id=&quot;dont-settle&quot;&gt;Don’t settle&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The temptation is strong.&lt;/strong&gt; It happens every time… &lt;strong&gt;somewhere after about a dozen mediocre interviews you might start thinking you just want anybody decent&lt;/strong&gt;. Resist! DON’T SETTLE. The management and culture hit will slow you down worse than not getting the help you need for a while. There are really great candidates out there – persevere to find them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Maybes = No’s&lt;/strong&gt;. If you’re not sold &amp;amp; excited about a candidate, there’s probably a good reason for it. Trust your gut. You should be excited about candidates to move forward. If you think of them as a “maybe”, that’s a great sign you should reject them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hiring the wrong people = VERY BAD.&lt;/strong&gt; Hiring the wrong person can really hurt you in a way that’s hard to recover from. The wrong hire can quickly poison your culture, frustrate your top performers, and set you down the wrong path.&lt;/p&gt;
&lt;h2 id=&quot;more-about-who&quot;&gt;More about “who”&lt;/h2&gt;
&lt;h3 id=&quot;first-few-hires&quot;&gt;First few hires…&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;First hires are the hardest roles to fill.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It’s hard to attract the very first few people. Early stage startups barely look like a real job to people. Most people don’t “get it” until there are already other people in the role.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Story:&lt;/em&gt; I’ve literally had a fresh out of college job candidate reject our offer (that he was excited about) because his mother didn’t think it was a “real job”.&lt;/p&gt;
&lt;p&gt;Most people also aren’t really a good fit for a really early stage startup. Tiny startups just aren’t for everyone. Most people need more structure and a more narrow job description than is typical for a very early startup employee.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First hires are the most important.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;They’ll impact the caliber of your future hires. Your first engineers will help hire and attract the next engineers.&lt;/p&gt;
&lt;p&gt;They will (or should) become leaders. It’s likely a bad sign for your first few hires if you cannot picture that person leading a project or team one day. Early in startups, you want people who can learn and grow quickly as your startup grows.&lt;/p&gt;
&lt;h3 id=&quot;find-your-bottlenecks&quot;&gt;Find your bottlenecks&lt;/h3&gt;
&lt;p&gt;Different stages of your company require different types of people.&lt;/p&gt;
&lt;p&gt;The first few engineers on our team were “Full Stack”. As a tiny team we needed engineers who could go both deep and wide. We were able to go deep in specific areas as needed, but also comfortable working across various parts of the tech stack.&lt;/p&gt;
&lt;p&gt;Hiring generalists worked well when the engineering team was very small, but as we grew it was time to start specializing. That doesn’t mean restricting people to one part of the tech stack, but rather it meant finding our software development bottlenecks and really focusing on finding some great people in the specific areas we were weakest in.&lt;/p&gt;
&lt;p&gt;At Close.io, our first specific bottleneck was on the infrastructure side. We had grown with so many customers, so much data, and a larger codebase to support that our engineering team was really slowing down trying to work on both the infrastructure &lt;em&gt;and&lt;/em&gt; the product itself. We eventually hired a couple really great DevOps/Infrastructure Engineers, and that dramatically helped.&lt;/p&gt;
&lt;p&gt;Next our bottleneck was Frontend Development (couldn’t keep up with Backend), then Design (we finally had frontend bandwidth to implement design work), then Backend, and most recently a Product Manager (for the first time it felt like customer problem discovery/definition was bottlenecking engineering effectiveness).&lt;/p&gt;
&lt;p&gt;What’s your biggest team bottleneck for building a better product? Focus your hiring there!&lt;/p&gt;
&lt;h3 id=&quot;mistake-focusing-only-on-technical-qualifications&quot;&gt;Mistake: Focusing only on technical qualifications&lt;/h3&gt;
&lt;p&gt;A mistake that I’ve made a couple times is focusing almost entirely on a candidate’s technical capabilities. It’s so hard to find programmers that &lt;a href=&quot;https://blog.codinghorror.com/why-cant-programmers-program/&quot;&gt;&lt;em&gt;actually&lt;/em&gt; know how to program&lt;/a&gt;, that when you find someone who passes your technical quality bar, the temptation is to assume they would be a great hire.&lt;/p&gt;
&lt;p&gt;But in reality, it’s incredibly important to pay attention to their written and verbal communication skills, maturity, attitude, desired work/life balance, culture, how they will work with people, their work processes, and other “soft skills” that separates a senior professional from just somebody who is good at programming. Do they have experience working in companies your size? If you’re remote, have they ever worked remotely before? Ask yourself if you actually think you would &lt;em&gt;like&lt;/em&gt; working with this person. Finally, always trust your gut.&lt;/p&gt;
&lt;p&gt;If you aren’t careful about qualifying candidates as an &lt;em&gt;entire person&lt;/em&gt; and not just based on their technical skills, you risk ruining your culture, building a team that is difficult to manage, doesn’t work well together, and who you can’t trust with important projects that aren’t 100% about the code.&lt;/p&gt;
&lt;h1 id=&quot;how-to-hire&quot;&gt;How to hire&lt;/h1&gt;
&lt;p&gt;The 6 steps for hiring are pretty obvious, so I’m going to just provide a few specific tips or hard lessons learned for each one.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Prep work&lt;/li&gt;
&lt;li&gt;Sourcing&lt;/li&gt;
&lt;li&gt;Screening&lt;/li&gt;
&lt;li&gt;Interviews&lt;/li&gt;
&lt;li&gt;Offer&lt;/li&gt;
&lt;li&gt;Onboarding (&amp;amp; beyond)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;how-to-stand-out&quot;&gt;How to stand out&lt;/h2&gt;
&lt;p&gt;Before we get into each step of how to hire, let me first say a couple words about standing out from other companies. Every other tech company is doing these same exact steps. Great candidates have hundreds of great jobs to consider. So it’s important to figure out how to make your company and role &lt;em&gt;stand out&lt;/em&gt; among the rest.&lt;/p&gt;
&lt;p&gt;Stand out by making the whole process fun and respectful, and play to your strengths.&lt;/p&gt;
&lt;h3 id=&quot;be-respectful&quot;&gt;Be respectful&lt;/h3&gt;
&lt;p&gt;The best way to stand out is to just treat all your candidates with respect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get back to people quickly.&lt;/strong&gt; People really appreciate if you always get back to them within a few days. Most companies don’t. Setup email templates to make this easy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Declined candidates should still walk away impressed.&lt;/strong&gt; Your goal should be to make everyone who interacts with your company to be impressed by you and want to tell their friends, even if they are a horrible fit themselves.&lt;/p&gt;
&lt;h3 id=&quot;make-it-fun&quot;&gt;Make it fun&lt;/h3&gt;
&lt;p&gt;Try to find a way to make the application and interview process fun for candidates. This could look different at different companies, but a couple quick ideas…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Make your interview questions connected in some way&lt;/strong&gt; so the candidate feels like they are making progress, rather than just being asked a series of unrelated technical questions.&lt;/p&gt;
&lt;p&gt;Examples: For one position at Close.io we have a 1 hour interview where we ask 12 connected questions that lead a candidate through architecting a real-world standalone web service. For another position, we have an application question that is related to the take-home challenge they’ll do in round 2, and our final interview is based on their work done in the take-home challenge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Help them learn about your company along the way.&lt;/strong&gt; The more you can share about your own company and the type of real-world challenges you experience, the more a candidate will understand if the role is a good fit for them (and hopefully be excited).&lt;/p&gt;
&lt;p&gt;Example: All our job applications require a candidate to send a POST with a specific JSON payload to an endpoint. The response payload returns not only the “answer” they need to apply, but also returns the URLs to a few engineering blog posts and open source projects of ours so they can learn more about us and our work.&lt;/p&gt;
&lt;h3 id=&quot;play-to-your-strengths&quot;&gt;Play to your strengths&lt;/h3&gt;
&lt;p&gt;There is something unique and attractive about your company or the specific role you’re hiring for. Something that can help you stand out as more interesting than the countless other jobs out there.&lt;/p&gt;
&lt;p&gt;Here are some examples of possible strengths:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unique tech stack (niche programming language, big data, using cutting edge frameworks, etc.)&lt;/li&gt;
&lt;li&gt;New technology area (e.g. virtual reality, cryptocurrency, AI)&lt;/li&gt;
&lt;li&gt;High salary, significant equity, or unusually strong benefits, etc.&lt;/li&gt;
&lt;li&gt;Building a new product from scratch&lt;/li&gt;
&lt;li&gt;Employee #1 who will work closely with founders and wear many hats&lt;/li&gt;
&lt;li&gt;Ability to work remotely or set own hours&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whatever it is for you… make sure you focus on sharing that. Advertise it clearly in your job description, mention it in your interviews, repeat it when you make a job offer, etc. Play to your strengths.&lt;/p&gt;
&lt;p&gt;For us at Close.io, we have several advantages over most companies: remote (work from anywhere, no set hours), good tech stack, and profitable. Sometimes we try to even fit these advantages into the job titles – e.g. we have advertised job posts with titles like “JavaScript/React Frontend Engineer @ a *profitable* fast-growing startup” which stands out among a sea of plain “Software Engineer” posts.&lt;/p&gt;
&lt;div class=&quot;wp-img&quot; style=&quot;max-width: 372px&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;372&quot; height=&quot;810&quot; src=&quot;https://philfreo.com/_astro/screenshot-2018-06-07-00.30.32.CAK0KeD1_1TFR8v.webp&quot; srcset=&quot;https://philfreo.com/_astro/screenshot-2018-06-07-00.30.32.CAK0KeD1_1TFR8v.webp 372w&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Outline of Hiring Plan for one position&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;prep-work&quot;&gt;Prep Work&lt;/h2&gt;
&lt;h3 id=&quot;plan-your-process&quot;&gt;Plan your process&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Who are you really looking for?&lt;/strong&gt; Make sure both you and the rest of your team have clear alignment on exactly what type of person you’re looking for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plan the entire hiring process upfront.&lt;/strong&gt; Once interviews start it will be much harder if you’re winging it.&lt;/p&gt;
&lt;p&gt;For the last position that I hired for, before talking to a single candidate or even posting a job advertisement, I first wrote a document with the outline shown here (to the right) and made sure the rest of the team was on board with not only &lt;em&gt;who&lt;/em&gt; we were looking for, but &lt;em&gt;why now&lt;/em&gt; and &lt;em&gt;how&lt;/em&gt; we would evaluate that person.&lt;/p&gt;
&lt;h3 id=&quot;write-the-job-description&quot;&gt;Write the job description&lt;/h3&gt;
&lt;p&gt;Take your time writing an excellent job post. It’s a mistake to just list out a bunch of requirements. Instead, be sure to:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sell your company &amp;amp; the role.&lt;/strong&gt; Remember that great candidates have lots of options – let them know why they should want to join your company.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Be specific about what they’ll do.&lt;/strong&gt; People don’t just want to know they’ll be writing Python backend code, they’ll want to know specifically what types of projects they will be doing and what their impact will be.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get feedback from people who are good at that role.&lt;/strong&gt; After writing your job post, send it around to people who you think are excellent at that role and ask for feedback before promoting it. This is especially important when hiring for a role that you yourself wouldn’t be qualified to fill.&lt;/p&gt;
&lt;h2 id=&quot;sourcing&quot;&gt;Sourcing&lt;/h2&gt;
&lt;p&gt;Where do you find candidates? 7 main places…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outbound vs Inbound.&lt;/strong&gt; You need to do both. But you have to treat outbound differently (more casual &amp;amp; more selling) in early stages of the process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your Network.&lt;/strong&gt; Your direct and 2nd degree network is already full of great people.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Directly reach out to people you think are great – &lt;em&gt;even/especially&lt;/em&gt; if you think they are happy in their current situation. Often they are more ready for a change than you may think. And worst case, they might be able to recommend somebody else great for the role.&lt;/li&gt;
&lt;li&gt;Get everyone in your company to do the same. When you hire someone new, one of the first things you should ask them is who else they know that might be a good fit for your open roles.&lt;/li&gt;
&lt;li&gt;Use social media. Not as a replacement for reaching out to people directly, but in addition.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A good example of how effective networking hiring can be is how we found our recent Product Manager hire, &lt;a href=&quot;https://twitter.com/heliostatic&quot;&gt;Ben&lt;/a&gt;. I posted the tweet below, and Ben was one of the people who “liked” the tweet. I didn’t directly know Ben, but he had “Product @ [startup]” in his Twitter Bio and had previously cofounded a company with a former colleague of mine. Since he liked my post, I sent him a Direct Message asking him if he knew anyone who might be good for our PM role. Soon enough we setup a call to discuss the role for himself! Not long after, Ben became our first PM and has been doing great.&lt;/p&gt;
&lt;p&gt;The most important part: Ben said that if I hadn’t sent him that message, he probably would have never applied!&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-width=&quot;500&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Hiring for our profitable💰 all-remote 🌎 team at &lt;a href=&quot;https://twitter.com/closeio?ref_src=twsrc%5Etfw&quot;&gt;@Closeio&lt;/a&gt;:&lt;br&gt;&lt;br&gt;🚀 Product Manager – be the first PM on a SaaS product used by thousands of salespeople&lt;a href=&quot;https://t.co/WQwbIuibly&quot;&gt;https://t.co/WQwbIuibly&lt;/a&gt;&lt;br&gt;&lt;br&gt;🚀Director of Marketing – lead our team doing content, product marketing, &amp;amp; more!&lt;a href=&quot;https://t.co/9MCSaUL2Hz&quot;&gt;https://t.co/9MCSaUL2Hz&lt;/a&gt;&lt;/p&gt;— Phil Freo (@philfreo) &lt;a href=&quot;https://twitter.com/philfreo/status/971115075032567808?ref_src=twsrc%5Etfw&quot;&gt;March 6, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;&lt;strong&gt;Job Boards.&lt;/strong&gt; This is the &lt;em&gt;easiest&lt;/em&gt; way to find candidates, since you basically just pay ~$300 and you get your ad in front of a lot of people who are looking for a job. The trick is to post on the &lt;em&gt;most relevant&lt;/em&gt; job boards for your specific role as possible. I suggest avoiding the very big name sites like Monster, and instead focusing on specific niche sites that have the most overlap with your ideal candidate as possible.&lt;/p&gt;
&lt;p&gt;For example, since our roles are remote, we post on remote-focused job boards like &lt;a href=&quot;http://WeWorkRemotely.com&quot;&gt;WeWorkRemotely.com&lt;/a&gt;, &lt;a href=&quot;http://RemoteOK.io&quot;&gt;RemoteOK.io&lt;/a&gt;, and &lt;a href=&quot;http://Remotive.io&quot;&gt;Remotive.io&lt;/a&gt;. For Frontend Development roles, we post on sites like &lt;a href=&quot;https://codepen.io/jobs/&quot;&gt;Codepen&lt;/a&gt;. For Designers, &lt;a href=&quot;https://dribbble.com/jobs&quot;&gt;Dribbble&lt;/a&gt;. You get the idea – the more targeted, the better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Targeted Communities.&lt;/strong&gt; Similar to job boards, there are online (and even offline) communities where your target audience hangs out that often you can informally share a job post with. This includes Slack groups, tech newsletters, MeetUps, etc. Often the organizer of these communities are happy to share highly targeted job opportunities with their members.&lt;/p&gt;
&lt;p&gt;When looking for targeted communities, consider: industry-specific, location-specific, or technology-specific.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Matching Sites.&lt;/strong&gt; There are some sites that serve as middle ground between inbound &amp;amp; outbound recruiting. Sites like &lt;a href=&quot;http://hired.com&quot;&gt;Hired&lt;/a&gt;, &lt;a href=&quot;https://angel.co/recruiting&quot;&gt;AngelList&lt;/a&gt;, &lt;a href=&quot;https://alist.co/&quot;&gt;AList&lt;/a&gt;, or &lt;a href=&quot;https://triplebyte.com/&quot;&gt;TripleByte&lt;/a&gt; serve as a place to find profiles of developers who are job seeking (and sometimes pre-screened) where you can browse for developers who look like a really good fit for you and reach out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Targeted Outbound&lt;/strong&gt;. Good old-fashioned cold emails to people who look like they would be the perfect fit for your role based on their exact experience. This is a lot of work but can be more effective than you may think.&lt;/p&gt;
&lt;p&gt;Tip: If you have open source projects that strangers have made helpful contributions to, reach out to them! If not, then reach out to contributors for open source projects for the libraries that you use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Recruiters.&lt;/strong&gt; If you go this route, just be very careful about how they are representing your company.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;622&quot; height=&quot;1576&quot; src=&quot;https://philfreo.com/_astro/screenshot-2018-06-03-23.55.46.Cep1cfme_Z2sNyQJ.webp&quot; srcset=&quot;https://philfreo.com/_astro/screenshot-2018-06-03-23.55.46.Cep1cfme_Z2sNyQJ.webp 622w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Example of relentless followup yielding fruit&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;relentless-follow-up&quot;&gt;Relentless follow-up&lt;/h3&gt;
&lt;p&gt;When you find someone you really want to work with… &lt;strong&gt;never stop following up&lt;/strong&gt;. Recruiting is like sales.&lt;/p&gt;
&lt;p&gt;The screenshot to the right shows an example of how I followed up with Casey, a developer that I had met a couple years prior and was really impressed with. You can see how every few months for ~19 months I kept checking in with him. At the bottom, you can see it eventually paid off. Casey is now a really key person on our engineering team.&lt;/p&gt;
&lt;p&gt;For two &lt;em&gt;more&lt;/em&gt; great examples of this, check out &lt;a href=&quot;http://blog.close.io/startup-hiring-follow-up&quot;&gt;Steli’s blog post on how he recruited me for &lt;em&gt;3 years&lt;/em&gt; before I finally joined him&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If there’s a single lesson here, it’s this: When you find amazing people, &lt;strong&gt;follow up and follow through with them&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;forever.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;screening&quot;&gt;Screening&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Use an Applicant Tracking System&lt;/strong&gt;. A dedicated ATS like &lt;a href=&quot;https://breezy.hr/&quot;&gt;Breezy.hr&lt;/a&gt;, &lt;a href=&quot;https://www.workable.com/&quot;&gt;Workable.com&lt;/a&gt;, or &lt;a href=&quot;https://www.lever.co/&quot;&gt;Lever.co&lt;/a&gt; will make collecting, screening, and tracking a large number of resumes/applicants much more efficiently than just using your inbox/Trello/spreadsheet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Customize your application form.&lt;/strong&gt; When advertising jobs online (&lt;em&gt;especially&lt;/em&gt; if you’re open to remote candidates), one of the toughest parts is actually having &lt;em&gt;too many&lt;/em&gt; applicants, where many of them are low quality. A good solution to this is to customize your job application form to make it fun for the right kind of person and turn away people who just want to blindly apply to as many jobs as possible.&lt;/p&gt;
&lt;p&gt;The form should, of course, give them a chance to share their &lt;strong&gt;resume/experience&lt;/strong&gt;, which you’ll pay close attention to when screening.&lt;/p&gt;
&lt;p&gt;But a good application form will also include &lt;strong&gt;technical screener questions&lt;/strong&gt; which should only take a few minutes for someone good to solve, but will quickly weed out those who aren’t qualified. We’ve done this and have actually gotten great feedback about it. People love little technical challenges in application since it makes it less boring to apply.&lt;/p&gt;
&lt;p&gt;Finally, include questions to help you evaluate their &lt;strong&gt;written communication&lt;/strong&gt;, which is often under-appreciated in engineering hiring.&lt;/p&gt;
&lt;p&gt;Having thoughtful questions on your application form will give you way more data when screening candidates.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1108&quot; height=&quot;1322&quot; src=&quot;https://philfreo.com/_astro/screenshot-2018-06-04-00.04.30.B3m_4BTE_1AfpoC.webp&quot; srcset=&quot;https://philfreo.com/_astro/screenshot-2018-06-04-00.04.30.B3m_4BTE_Z1s4je2.webp 640w, https://philfreo.com/_astro/screenshot-2018-06-04-00.04.30.B3m_4BTE_1OvOx6.webp 750w, https://philfreo.com/_astro/screenshot-2018-06-04-00.04.30.B3m_4BTE_Z1JUETb.webp 828w, https://philfreo.com/_astro/screenshot-2018-06-04-00.04.30.B3m_4BTE_2dvTXq.webp 1080w, https://philfreo.com/_astro/screenshot-2018-06-04-00.04.30.B3m_4BTE_1AfpoC.webp 1108w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Customized application form with screener questions&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;interviews&quot;&gt;Interviews&lt;/h2&gt;
&lt;p&gt;Example flow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Phone Screen&lt;/strong&gt; (30-60m) – Intro, quick technical screener questions, hear about their experience and determine whether they seem overall impressive.
&lt;ul&gt;
&lt;li&gt;Get good at phone screens – this is where most of your recruiting time will be spent.&lt;/li&gt;
&lt;li&gt;Have good technical screener questions that get you to a “No” as quickly as possible if someone isn’t great. For me I like to ask something one level deeper than frameworks might take care of for you, e.g. asking about SQL escaping/injection (for people used to ORMs), or direct DOM manipulation (for people used to React).&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://coderpad.io/&quot;&gt;coderpad.io&lt;/a&gt; for quick “can they actually code” live coding exercises of something simple.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technical Interview 1&lt;/strong&gt; (60m) – In depth technical/architectural questions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Take-home project&lt;/strong&gt; (2-3h) – Let’s see them actually build something (2-3 hrs). It’s important to timebox these for candidates so they don’t feel taken advantage of. Some companies actually pay. If you aren’t paying them, then don’t give a real problem that could be misinterpreted as trying to get free work from them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technical Interview 2&lt;/strong&gt; (60m) – Drill in on missing technical areas, evaluate softer skills, overall seniority, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Things to keep in mind during interviews:&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;best interviews should feel like a dialog&lt;/strong&gt; rather than just you asking a bunch of technical questions in a row. For example: Let them tell you about their experience, and ask questions along the way to go deeper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Remember to sell.&lt;/strong&gt; Use every interview as a chance to share what you think is great about your company and this role, since great candidates can have many opportunities to choose from.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Let them ask good questions&lt;/strong&gt;. They should have good ones and be engaged. No questions is a bad sign.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gut check / do you like them?&lt;/strong&gt; If something doesn’t feel right during an intro call (when everyone is trying to put their best foot forward) that’s usually a sign of a bad fit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Have all interviewers leave detailed notes &amp;amp; a summary/conclusion.&lt;/strong&gt; It’s best if each interviewer independently writes their thoughts immediately following interviews to avoid groupthink. Press interviewers to summarize with a simple rating scale. For example we use language like “strong like” or “weak dislike”. This allows you to review everyone’s feedback afterwards and reject anyone where most interviewers are only ambivalent toward a candidate.&lt;/p&gt;
&lt;h2 id=&quot;offer&quot;&gt;Offer&lt;/h2&gt;
&lt;p&gt;So you found someone you like? Great! Now comes the pivotal offer stage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Always check references.&lt;/strong&gt; Before you make the offer, reference checks are key to validate that someone is as good as you think they are, and not just good at interviewing. Great people with a decade or more of experience should &lt;em&gt;easily&lt;/em&gt; be able to come up with a long list of names of current or past coworkers that would happily vouch for them.&lt;/p&gt;
&lt;p&gt;Cautionary tale 1: Early in Close.io’s history we made a hire that turned out to be really, really terrible. In hindsight, I’m sure we would have avoided this hire if we had done thorough reference checks. Sadly, we also saw him go on to get hired (and also eventually fired) by another startup we respected. Had they talked to us first, they could have also avoided wasting time.&lt;/p&gt;
&lt;p&gt;Cautionary tale 2: We were once trying to move quickly with a candidate, so we made an offer first but contingent upon reference checks. It turns out that once we did references, we detected that there was a significant error on his resume about the duration of time he spent in the role that was most relevant to us.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Make the offer &amp;amp; close it&lt;/strong&gt;. Remember to focus the offer not only on the salary &amp;amp; benefits, but especially on selling the opportunity, quality of the team, and their ability for learning/growth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trial periods.&lt;/strong&gt; If you have the opportunity to do a contract / trial period with someone before jumping to a full-time offer, that can be a really great lower risk way to see if they are truly a good fit (and if you are a good fit for them). Not all candidates can risk leaving a good job for a 1 month contract, but when possible, this can be a very useful tool.&lt;/p&gt;
&lt;h3 id=&quot;mistake-assuming-that-accepted-offer--position-filled&quot;&gt;Mistake: Assuming that accepted offer = position filled&lt;/h3&gt;
&lt;p&gt;It’s a rookie mistake to assume that just because a candidate accepts your job offer, that you’ve actually finished your job hiring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Recruiting doesn’t stop at signed offer.&lt;/strong&gt; Believe it or not, I’ve had more than one example where a candidate accepts an offer (including signing paperwork) and then changes their mind before their start date. I’ve heard reasons like “I got more excited by a different company”, “my current employer made a counteroffer too good to refuse”, and “my spouse ended up not being okay with the retreat travel requirements”. Don’t get mad, just move on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You’ll have to make more offers than you think.&lt;/strong&gt; Between some offers being declined, and some candidates dropping out before their start date, you will end up needing to make more offers than positions you’re trying to fill. While you definitely shouldn’t make more offers than roles you’re prepared to hire for, I do recommend that you continue talking to candidates even after you’ve made an offer, until you have somebody on board doing a great job.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sell to start date.&lt;/strong&gt; Keep candidates engaged from offer to start date by checking in along the way and continuing to sell. Specifically, we make sure that in the days/weeks between offer and start date that we send candidates at least a couple emails with links to blog posts we wrote, links to 5 star reviews of our product left by customers, a keynote video from our CEO, etc.&lt;/p&gt;
&lt;h2 id=&quot;onboarding--beyond&quot;&gt;Onboarding &amp;amp; Beyond&lt;/h2&gt;
&lt;h3 id=&quot;mistake-assuming-that-your-hires-will-work-out&quot;&gt;Mistake: Assuming that your hires will work out&lt;/h3&gt;
&lt;p&gt;For the candidates that do make it to their start date, unfortunately many will still not work out – probably a higher percentage than you may think. Either you or they will determine the role isn’t right for them.&lt;/p&gt;
&lt;p&gt;Your work doesn’t stop at their start date – focus on post-hire success. The first 1-2 months of onboarding is critical!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don’t assume new hires are happy.&lt;/strong&gt; Be checking in with new hires frequently. Ask questions like “We’re a couple weeks in and you’ve gotten a better sense for us now — what’s different than what you expected, good and bad?” New hires just may realize the job isn’t for them. For example, we’ve had someone tell us “I learned more in the past 3 months than the 3 years before that – but the pace is not for me”. Do what you can to make people happy, but then move on if it’s the wrong fit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Learn from each bad hire.&lt;/strong&gt; Your goal from each bad hire is to learn how you can improve your hiring process to avoid similar mistakes in the future.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Make sure you’re still excited after 30 days (or fire fast)&lt;/strong&gt;. Realistically, you’ll never get a perfect sense of someone from a few hours of interviews. So after you’ve had some time actually working with someone, if you’re not still excited about them being on the team, fire fast so you both can move on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hire more people than you think you need.&lt;/strong&gt; Realizing that many hires don’t work out, you should plan to hire more people than you actually need in order to end up with the people you really need. Of course, you need to be able to afford everyone that you hire. But where you have room to hire an extra person (e.g. hiring 3 instead of 2 people for a role), doing so will increase your odds of success. &lt;strong&gt;It’s much easier to hire in parallel than serial for the same position&lt;/strong&gt;, so it’s also more efficient to over-hire upfront than to keep restarting your hiring process after a hire doesn’t work out.&lt;/p&gt;
&lt;h3 id=&quot;onboarding-tips&quot;&gt;Onboarding tips&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Have a well thought out onboarding plan.&lt;/strong&gt; I create a Dropbox Paper doc with a long list of checkboxes with a combination of things that they should do (project work) and learn (reading, talking to specific people, etc.) broken down by day and week for the first month.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ship on day 1.&lt;/strong&gt; Get new engineers to ship something (even if super tiny) on their first day if at all possible. Definitely within the first week.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Start with small quick wins but rapidly give them increasingly difficult projects.&lt;/strong&gt; Get them in the habit of moving fast and shipping, quickly working toward giving them big amounts of responsibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spend a lot of time with them.&lt;/strong&gt; Check in multiple times per week. If hiring remotely, try to find a way to start out in-person if possible.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;managingrunning-a-growing-team&quot;&gt;Managing/running a growing team&lt;/h3&gt;
&lt;p&gt;Eventually all your hard work hiring will pay off and you’ll start to have a larger team. Now what? Leading a growing team is where things get fun. This topic really deserves its own blog post, but here are a few quick principles that have helped our team scale…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hand over responsibilities.&lt;/strong&gt; The sooner you trust your team with big problems and stop trying to control everything yourself, the better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Have frequent 1:1s.&lt;/strong&gt; Give the people you manage a meeting every week or two that is primarily their time. Discuss challenges and how they are feeling about work. Ask questions and listen. Tip: I like to keep an Apple Note per person on my team and when I think about something to discuss with them (a question or positive or negative feedback) throughout the week I’ll jot it there, and then use that Note before/after the 1:1 to take notes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Build camaraderie.&lt;/strong&gt; Especially with a remote team, it’s important that everyone feels connected and like they are a part of building something great together. Here are a few things that have helped us accomplish that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Retreats.&lt;/strong&gt; 2-3 times per year we get everyone physically together in one place for about a week (different places around the world). This is where the most important team bonding, vision setting for the year, etc. occurs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retrospectives.&lt;/strong&gt; We try to (but need to do them more often!) have open discussions after projects wrap up where we discuss how it went, what we learned, what we did well, and what we’d like to do better as a team next time. The goal is to have a culture of continually seeking growth and improvement through healthy/safe discussions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Show &amp;amp; Tells.&lt;/strong&gt; When you’re tiny, everyone knows what everyone else is working on. After you grow beyond a few people that’s no longer possible, so having a regular time where people are encouraged to share details of what they’ve been learning/coding/designing/launched/etc. is a fun way to keep people on the same page and decrease &lt;a href=&quot;https://en.wikipedia.org/wiki/Bus_factor&quot;&gt;bus factor&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Highs &amp;amp; Lows.&lt;/strong&gt; Recently we’ve introduced a time where each person on the team very quickly (~30s/person) shares their High (best part) and Low (worst part) of their past week – related to either their work or personal life. Especially for our remote team, this has been great for hearing about wins and getting a better sense of what everyone is struggling with.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Add structure as the team grows.&lt;/strong&gt; Setup Project Leads, Tech Leads, Engineering Managers, etc. so that you can give people on your team more responsibility and ensure you don’t bottleneck too many things.&lt;/p&gt;
&lt;h2 id=&quot;remote-hiring&quot;&gt;Remote hiring&lt;/h2&gt;
&lt;p&gt;Being a distributed / all-remote team has been a huge asset for us both in terms of recruiting as well as retention.&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-width=&quot;500&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;That special and kind of sad moment in a growing, &lt;a href=&quot;https://twitter.com/hashtag/remote?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#remote&lt;/a&gt;, company&apos;s life when everyone doesn&apos;t even fit on a single screen in &lt;a href=&quot;https://twitter.com/zoom_us?ref_src=twsrc%5Etfw&quot;&gt;@zoom_us&lt;/a&gt; anymore. &lt;a href=&quot;https://twitter.com/closeio?ref_src=twsrc%5Etfw&quot;&gt;@Closeio&lt;/a&gt; &lt;a href=&quot;https://t.co/Wb54y8gSIs&quot;&gt;pic.twitter.com/Wb54y8gSIs&lt;/a&gt;&lt;/p&gt;— Phil Freo (@philfreo) &lt;a href=&quot;https://twitter.com/philfreo/status/948605461292552194?ref_src=twsrc%5Etfw&quot;&gt;January 3, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;&lt;strong&gt;Really great people are everywhere.&lt;/strong&gt; We’ve found amazing candidates from countries all around the world. The Close.io Product/Engineering team is 14 people – all remote – working from 6 different countries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Way more candidates.&lt;/strong&gt; We pretty quickly get hundreds of candidates for every remote position we advertise. There are just &lt;em&gt;a lot&lt;/em&gt; of people in the world who want to work remotely. (This is a blessing and a curse, see the “Screening” section.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You can really stand out as a top company.&lt;/strong&gt; It was very hard to stand out in Silicon Valley among all the other tech startups hiring. But among remote-friendly companies we were easily able to stand out as a very attractive option for candidates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cheaper.&lt;/strong&gt; SFBay area engineering salaries, housing, cost of living, and office space are all insanely expensive. Go remote and hire more great people!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Retain employees longer&lt;/strong&gt;. People stick around longer, since they can live where they want, move around as needed, and don’t have to deal with commutes or distraction-prone offices. At Close.io, once we went remote, me and 3 other people on our team moved out of California when family or other needs arose. Our jobs stayed the same even as our locations changed drastically.&lt;/p&gt;
&lt;p&gt;For me, working from home has been wonderful as I became a father last year.&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-width=&quot;500&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;I’m only 2 months in &lt;a href=&quot;https://twitter.com/hashtag/dadlife?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#dadlife&lt;/a&gt;, but my thoughts so far:&lt;br&gt;– working from home allows lunch &amp;amp; 5m coffee breaks to be special moments I can see/play with him&lt;br&gt;– time with him first thing in the morning is special&lt;br&gt;– it gives you a reason to be more focused with the work time you have &lt;a href=&quot;https://t.co/byGOya0RC5&quot;&gt;https://t.co/byGOya0RC5&lt;/a&gt;&lt;/p&gt;— Phil Freo (@philfreo) &lt;a href=&quot;https://twitter.com/philfreo/status/953974177563840512?ref_src=twsrc%5Etfw&quot;&gt;January 18, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Building and growing an engineering team is hard… but it does get easier. And seeing your new team be able to accomplish more than you could ever before is a really great feeling.&lt;/p&gt;
&lt;p&gt;Do you have your own tips for growing an engineering team? Questions or want to hear more? &lt;a href=&quot;https://twitter.com/intent/tweet?text=@philfreo+&quot;&gt;Tweet at me&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Engineering</category><category>Hiring</category><category>Leadership</category><category>Remote</category><category>Startups</category></item><item><title>My InsideSalesSummit interview: How sales and engineering teams should work together</title><link>https://philfreo.com/blog/my-insidesalessummit-interview-how-sales-and-engineering-teams-should-work-together/</link><guid isPermaLink="false">https://philfreo.com/?p=742</guid><description>I did an interview for the Close.io Inside Sales Summit, a free 5-day virtual summit with perspectives from 50+ leaders on inside sales. Ryan Robinson interviewed me for an…</description><pubDate>Thu, 16 Nov 2017 19:25:25 GMT</pubDate><content:encoded>&lt;p&gt;I did an interview for the &lt;a href=&quot;https://www.insidesalessummit.com/&quot;&gt;Close.io Inside Sales Summit&lt;/a&gt;, a free 5-day virtual summit with perspectives from 50+ leaders on inside sales. Ryan Robinson interviewed me for an engineer’s perspective on topics related to sales, including how sales teams and engineering teams can work best together.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.insidesalessummit.com/interviews/wednesday/phil-freo/&quot;&gt;&lt;strong&gt;Watch the interview here&lt;/strong&gt;&lt;/a&gt;: &lt;a href=&quot;https://www.insidesalessummit.com/interviews/wednesday/phil-freo/&quot;&gt;https://www.insidesalessummit.com/interviews/wednesday/phil-freo/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Overview:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In this interview, Phil digs into his personal experience to share what he believes are the best ways for sales and engineering teams to work together, including how to pitch (in-demand) feature requests to product and engineering and how reps should address feature requests from high-value prospects. Phil also explains why technical founders need to care about sales, and goes over common misconceptions about both engineers &amp;amp; salespeople.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interview Length:&lt;/strong&gt; 19:14&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Topics Covered:&lt;/strong&gt; The best ways for sales and engineering teams to work together, how to pitch (in-demand) feature requests to product and engineering, how to address feature requests from high-value prospects, why technical founders need to care about sales, and common misconceptions about engineers &amp;amp; salespeople.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.insidesalessummit.com/interviews/wednesday/phil-freo/&quot;&gt;&lt;img alt=&quot;Video: my Inside Sales Summit interview&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2136&quot; height=&quot;2212&quot; src=&quot;https://philfreo.com/_astro/screenshot-2017-11-28-19.34.005.DqbEM46G_1zcp2A.webp&quot; srcset=&quot;https://philfreo.com/_astro/screenshot-2017-11-28-19.34.005.DqbEM46G_Z1KHwuP.webp 640w, https://philfreo.com/_astro/screenshot-2017-11-28-19.34.005.DqbEM46G_ZG4dfF.webp 750w, https://philfreo.com/_astro/screenshot-2017-11-28-19.34.005.DqbEM46G_22Szcp.webp 828w, https://philfreo.com/_astro/screenshot-2017-11-28-19.34.005.DqbEM46G_5WyyP.webp 1080w, https://philfreo.com/_astro/screenshot-2017-11-28-19.34.005.DqbEM46G_1B5C19.webp 1280w, https://philfreo.com/_astro/screenshot-2017-11-28-19.34.005.DqbEM46G_1fbUP.webp 1668w, https://philfreo.com/_astro/screenshot-2017-11-28-19.34.005.DqbEM46G_2wGU7F.webp 2048w, https://philfreo.com/_astro/screenshot-2017-11-28-19.34.005.DqbEM46G_1zcp2A.webp 2136w&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>Close.io</category><category>Entrepreneurship</category><category>SaaS</category><category>Startups</category></item><item><title>How to log Feature Requests</title><link>https://philfreo.com/blog/feature-requests/</link><guid isPermaLink="false">https://philfreo.com/?p=722</guid><description>Below is an internal team post I wrote to help us at Close.io do a better job of capturing customer’s feature requests in a way that leads to better product development outcomes…</description><pubDate>Tue, 24 Oct 2017 22:53:44 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Below is an internal team post I wrote to help us at &lt;a href=&quot;https://close.io/&quot;&gt;Close.io&lt;/a&gt; do a better job of capturing customer’s feature requests in a way that leads to better product development outcomes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I’m sharing this publicly because I believe this advice can help other startups and SaaS product companies as well.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When a customer request for a new feature come in, it’s easy to not log it at all (“we’ve heard that one before”) or to log it in a suboptimal way. Here are a few details on how to &lt;em&gt;best&lt;/em&gt; capture customer needs and why it matters.&lt;/p&gt;
&lt;h1 id=&quot;the-typical-but-unhelpful-way&quot;&gt;The typical, but unhelpful, way&lt;/h1&gt;
&lt;p&gt;The most common way feature requests are logged is in a “light” format like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;“&lt;a href=&quot;mailto:john@example.com&quot;&gt;john@example.com&lt;/a&gt; wants feature FOO – [link to ticket]”&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While this does provide a list of people to notify if we ever do launch a feature that we happen to call “FOO”, it falls short in many other ways.&lt;/p&gt;
&lt;p&gt;This is typically very unhelpful for Product Development or for getting a customer’s needs solved because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Many people have different ideas about what this feature specifically could be.&lt;/li&gt;
&lt;li&gt;There are different ways a feature could work and this doesn’t give any insight into which way would be best.&lt;/li&gt;
&lt;li&gt;Frequently there’s a different, better way that their underlying need (use case) could be met instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Logging requests in this “light” format may seem at least like a good way to build a notification list, but in reality leads us to building and notifying people about something that turns out not to best solve their core problems.&lt;/p&gt;
&lt;p&gt;Similarly, it may seem that this at least gives us a “votes” system to prioritize Product decisions, but in reality there isn’t enough detailed signal in the votes to design a feature we can be confident really helps most of them.&lt;/p&gt;
&lt;p&gt;Caveat: Logging a feature request with “email address only” is &lt;em&gt;still&lt;/em&gt; better than not logging any at all, because it at least does give us a list of people we can later contact for more information.&lt;/p&gt;
&lt;h1 id=&quot;the-better-way&quot;&gt;The better way&lt;/h1&gt;
&lt;p&gt;A feature request logging format that is 100 times more helpful is one that includes details of the &lt;em&gt;use case&lt;/em&gt;. A use case should include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Who is the user and company, and what’s the user’s role within the company?&lt;/li&gt;
&lt;li&gt;What situation is causing the user to want this feature? (When did the need start?)&lt;/li&gt;
&lt;li&gt;In their own words, what do they mean by “feature FOO”?&lt;/li&gt;
&lt;li&gt;How, specifically, would they use this feature?&lt;/li&gt;
&lt;li&gt;Really, why do they want this feature? What is the specific use case?&lt;/li&gt;
&lt;li&gt;What is the business value they’re hoping to achieve because of this feature?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;It all really boils down to asking “&lt;em&gt;why?&lt;/em&gt;” and then logging in as much detail as possible, &lt;em&gt;in the customer’s own words&lt;/em&gt;, the specifics about what they are &lt;em&gt;actually&lt;/em&gt; trying to accomplish.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;“In the customer’s own words” is really important because it’s easy for us to, in the moment, substitute our own current idea of a solution, when what’s important is logging their actual problem and goals. Having the customer’s own words helps us avoid bias.&lt;/p&gt;
&lt;h1 id=&quot;how-this-helps&quot;&gt;How this helps&lt;/h1&gt;
&lt;h2 id=&quot;demand-based-product-development&quot;&gt;Demand based Product Development&lt;/h2&gt;
&lt;p&gt;Having concise but detailed use cases help direct us toward clarity when we try to validate whether a Product Proposal makes sense to move forward with. They help answer:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“Is there really a pattern of consistent use cases that are important enough AND that our specific idea of solution would be a GREAT solution to?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When logging feature requests, it’s best to assume that, by default, no feature will get developed until there is a set of documented specific use cases with reasons/explanations on why it’s important to that customer and how they would use it.&lt;/p&gt;
&lt;p&gt;From a Product Development perspective, it’s important that we pay most attention to the underlying need (demand) rather than customer’s ideas and requests for specific features (supply). (For more about this, there’s a great &lt;a href=&quot;https://demandthinking.com/episodes/2017/7/23/episode-1-think-you-might-be-building-the-wrong-thing-youve-misinterpreted-demand&quot;&gt;podcast&lt;/a&gt; on the subject).&lt;/p&gt;
&lt;h2 id=&quot;design-decisions&quot;&gt;Design Decisions&lt;/h2&gt;
&lt;p&gt;Even in times where there’s clear demand in a problem area and many requests for a particular feature, another factor is at play where detailed feature requests with use cases help tremendously.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Often, there are multiple &lt;em&gt;very&lt;/em&gt; different ways a feature could work, but without understanding &lt;em&gt;exactly&lt;/em&gt; what users are trying to achieve, it’s hard to know which way is better.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most features which seem clear and straightforward on the surface can go in different directions once you really dig into the nitty gritty UI/UX or technical design. Being able to go back to the core use cases (specific real-world things our customers are trying to do) it helps us quickly and confidently choose a direction.&lt;/p&gt;
&lt;h1 id=&quot;who-should-do-this&quot;&gt;Who should do this&lt;/h1&gt;
&lt;p&gt;The Product team takes ultimate responsibility for fleshing out customer use cases and validating solutions with customers.&lt;/p&gt;
&lt;p&gt;Anyone who talks with customers, however (especially Support, Success, and Sales), is in a unique position to already have many interactions where feature requests and customer problems come up. It is immensely helpful and valuable for moving the product forward in the right ways if these interactions result in feature requests logged with use cases. We love having the entire company championing customer needs and giving input into Product direction. This is &lt;em&gt;the&lt;/em&gt; best way to do that.&lt;/p&gt;
&lt;p&gt;At Close.io, the best place to log feature requests with uses cases is in our “Feature Requests &amp;amp; Customer Needs” Trello Board. If you’re ever inclined to write a Product Proposal, including a few curated use cases (in the customer’s own words) is useful to supporting the idea.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;What do you think? &lt;a href=&quot;https://twitter.com/intent/tweet?text=%40philfreo%20&quot;&gt;Send me a tweet&lt;/a&gt; about how your team captures customer feature requests.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>Product</category><category>SaaS</category><category>Startups</category></item><item><title>Don’t punish old trials and former customers</title><link>https://philfreo.com/blog/dont-punish-old-trials/</link><guid isPermaLink="false">https://philfreo.com/?p=686</guid><description>A common pattern in SaaS apps is to allow a free trial period of 2 weeks or 1 month, and then to require a credit card to use it any longer. Either out of curiosity or out of a…</description><pubDate>Fri, 07 Aug 2015 23:55:47 GMT</pubDate><content:encoded>&lt;p&gt;A common pattern in SaaS apps is to allow a free trial period of 2 weeks or 1 month, and then to require a credit card to use it any longer.&lt;/p&gt;
&lt;p&gt;Either out of curiosity or out of a genuine need for a tool that some SaaS service is offering, I will often sign up for a free trial soon after learning about it in order to check it out. For a variety of reasons by the time the free trial is up, I’m not ready to purchase.&lt;/p&gt;
&lt;p&gt;It could be because I was just poking around. But more often it’s because I got too busy. Or my reason for signing up didn’t stay a high enough priority to be ready to purchase and fully implement some solution. Or maybe because the product just wasn’t far enough developed to satisfy what I was looking for.&lt;/p&gt;
&lt;p&gt;What I find happening is that 3, 6, 12, or 18 months later I’ll find myself thinking about this tool. Perhaps the problem that led me to originally check out tools of the service has become more pressing than ever before. Or perhaps I’m fed up with another tool I chose, and am searching again for a better option. Or I’m hoping that the product has evolved more. Or whatever.&lt;/p&gt;
&lt;p&gt;When logging back into your previously-created account, what you typically see is something like this:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your free trial expired. Please enter your credit card to continue.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At this point, it’s far too easy to just close the tab. I’ve done it many times, even when I actually was in need (and willing to purchase) a tool in their category.&lt;/p&gt;
&lt;p&gt;Savvy users will email the site’s sales or support team and can usually get a trial extended, but this often takes a few hours, which sucks. &lt;em&gt;When a user gives you enough attention to want to check out your product right now, you should always &lt;strong&gt;take advantage&lt;/strong&gt; of that. It’s too easy for that attention to get lost if you make the user wait.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Similarly some users will just use another email address, which is really bad for understanding your marketing funnel and metrics, and is a bad user experience overall. Plus, this may mean losing whatever progress was made on the first trial.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Let’s stop punishing our older trials.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I always thought this was a bad user experience, but I knew we were guilty of doing the same thing at &lt;a href=&quot;http://close.io/&quot;&gt;Close.io&lt;/a&gt;. Not anymore. Now if you login to a trial that has been expired for long enough (i.e. you haven’t checked out the product in a long time), we give you a single click to get started again.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://philfreo.com/wp-content/uploads/2015/08/Screenshot-2015-08-04-14.50.22.png&quot;&gt;&lt;img alt=&quot;Screenshot 2015-08-04 14.50.22&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;630&quot; height=&quot;212&quot; src=&quot;https://philfreo.com/_astro/screenshot-2015-08-04-14.50.22.BIvalNNs_Z1uahEs.webp&quot; srcset=&quot;https://philfreo.com/_astro/screenshot-2015-08-04-14.50.22.BIvalNNs_Z1uahEs.webp 630w&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The same applies for former customers. Once you’ve been around a while, it won’t be uncommon for an early adopter to have churned and then want to give you another shot a year later. We should welcome this!&lt;/p&gt;
&lt;p&gt;It’s a super easy change to make and within minutes of pushing this change we already saw its effects pushed to our chat.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://philfreo.com/wp-content/uploads/2015/08/Screenshot-2015-08-07-16.53.17.png&quot;&gt;&lt;img alt=&quot;Screenshot 2015-08-07 16.53.17&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1334&quot; height=&quot;108&quot; src=&quot;https://philfreo.com/_astro/screenshot-2015-08-07-16.53.17.4sYiZ6xr_23II2n.webp&quot; srcset=&quot;https://philfreo.com/_astro/screenshot-2015-08-07-16.53.17.4sYiZ6xr_ZegzY2.webp 640w, https://philfreo.com/_astro/screenshot-2015-08-07-16.53.17.4sYiZ6xr_1KXEoV.webp 750w, https://philfreo.com/_astro/screenshot-2015-08-07-16.53.17.4sYiZ6xr_Rxdc6.webp 828w, https://philfreo.com/_astro/screenshot-2015-08-07-16.53.17.4sYiZ6xr_15KoM0.webp 1080w, https://philfreo.com/_astro/screenshot-2015-08-07-16.53.17.4sYiZ6xr_27pev9.webp 1280w, https://philfreo.com/_astro/screenshot-2015-08-07-16.53.17.4sYiZ6xr_23II2n.webp 1334w&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Don’t treat old users worse than new trials!&lt;/p&gt;
</content:encoded><category>Close.io</category><category>Product</category><category>SaaS</category><category>Startups</category></item><item><title>Why you should Archive your emails when you’re done with them</title><link>https://philfreo.com/blog/archive-your-emails/</link><guid isPermaLink="false">https://philfreo.com/?p=677</guid><description>There are two types of people in this world: People who archive incoming emails when they are finished with them, and try to achieve “inbox zero” People who leave all incoming…</description><pubDate>Tue, 23 Jun 2015 23:02:34 GMT</pubDate><content:encoded>&lt;p&gt;There are two types of people in this world:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;People who archive incoming emails when they are finished with them, and try to achieve “inbox zero”&lt;/li&gt;
&lt;li&gt;People who leave all incoming email in their “inbox” forever but use “Mark as Unread” or Star/Flag message to indicate items still requiring action&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you’re a Type #1 person, you can stop reading now.&lt;/p&gt;
&lt;p&gt;If are a “never archive anything” type person, I’d like to make a case for why you should become an “inbox zero” type person and archive emails when you’re done with them.&lt;/p&gt;
&lt;p&gt;You may be thinking, “who are you to tell me how to manage my email!?” It’s true, I have no right to that, and I understand that different people have different workflows. But I have heard all the reasons and still strongly believe all Type #2 people would be better off if they become Type #1 people.&lt;/p&gt;
&lt;p&gt;I have made my case to several people over the last few years and all of them that have ever tried it have thanked me later. I believe there is really no downside to becoming a Type #2 person and only upside.&lt;/p&gt;
&lt;p&gt;I know that not everybody is used to archiving their email, but I’d like to make a case to you that it’s not any more difficult and that if you give it a shot, it will really help your workflow. First, let me address the top 3 reasons people usually give for why they don’t archive currently:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“Archiving is an unnecessary extra step”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The thing is, it’s not really an extra step to archive an email once you’re done with it! From Gmail it’s a single keyboard shortcut or a single click of the Archive button. You’d have to click Back anyway so it’s 0 additional steps to archive a message instead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“I keep emails in my inbox so I can find them later easily”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Emails are still searchable even when archived, and you can see all those messages in Gmail under All Mail. Archived mail is not lost, it can still be easily browsed and searched.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“I treat ‘unread’ or ‘starred’ messages as my actionable items list, which is just as good as archiving”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Abusing&lt;/em&gt; the “Unread” feature to keep emails around that you actually did read but just haven’t finished handling / responding to yet is unwise for two reasons. First, it prevents you from having a true list of &lt;em&gt;actually&lt;/em&gt; unread emails.&lt;/p&gt;
&lt;p&gt;Second, using Unread status or Stars/Flags to indicate actionable items means that your inbox is a mix of “emails I’m done with” and “really important emails I shouldn’t forget to handle or respond to”. Those items are interspersed. Sure, having items as bold (unread) or starred can make the important ones stand out, but what happens if you don’t remember to handle them before they end up on page 2? With an infinite inbox, it’s too easy to lose messages that you saw but didn’t handle yet.&lt;/p&gt;
&lt;p&gt;Even if you always handle these items before they get too far back in your inbox, it means you’re doing lots of extra mental work by having to continually scan your inbox for items that need handling. You have to keep seeing the already handled items unnecessarily which means more things for your brain to process. Wouldn’t it be better if there was a single concise list of emails requiring further action?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why archiving is better…&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We’ve established that there’s really no downside to archiving all email once you’re done with it. Let’s talk about the upside.&lt;/p&gt;
&lt;p&gt;When you archive messages you’re finished with, what you’re left with is an inbox that consists &lt;em&gt;only&lt;/em&gt; of actionable items. No more continually re-scanning past dozens of emails that are no longer important, in search of the one you need to reply to. No more lying to your email client saying you haven’t read an email after you really have. No more losing important emails in the stream.&lt;/p&gt;
&lt;p&gt;Archiving makes your inbox &lt;em&gt;the&lt;/em&gt; list for all your emails requiring action, and nothing else.&lt;/p&gt;
&lt;p&gt;I bet if you give it a shot, you’ll like it within a week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How to get started&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Start out by doing a bulk archive of everything in your inbox that you know has been handled. In Gmail there’s a Select All checkbox, then a second link appears at the top to &lt;em&gt;truly&lt;/em&gt; select all. Then you can click Archive.&lt;/p&gt;
&lt;p&gt;When a new email comes in, of course you first need to read it (or at least skim the subject/sender). If you decide you’ve read enough and it requires no action, then simply hit Archive! If you’re done your part and replied to a message, hit Archive! If the email represented a task and you finished the task, hit Archive! If it’s a newsletter and you finished reading it, hit Archive! (If it’s a newsletter or promotional email you never care to read, then Unsubscribe… and then Archive!)&lt;/p&gt;
&lt;p&gt;Now, you can look at your inbox anytime to see and handle emails that still need more reading, responding, or action.&lt;/p&gt;
&lt;p&gt;Let me know how it goes!&lt;/p&gt;
&lt;p&gt;– Phil&lt;/p&gt;
&lt;p&gt;P.S. The best products, in my opinion, encourage an opinionated workflow that promotes success. It’s from this workflow that we built &lt;a href=&quot;http://blog.close.io/introducing-inbox&quot;&gt;Inbox in Close.io&lt;/a&gt;, which syncs your CRM and your email account for an Inbox of actionable sales-focused items.&lt;/p&gt;
</content:encoded><category>Productivity</category></item><item><title>HackToStart Podcast</title><link>https://philfreo.com/blog/hacktostart-podcast/</link><guid isPermaLink="false">https://philfreo.com/?p=674</guid><description>I got interviewed for my first podcast recently. It’s called HackToStart, and it’s one that I’ve been listening to for a while now, so I was excited to be a guest on the show…</description><pubDate>Tue, 23 Jun 2015 21:22:10 GMT</pubDate><content:encoded>&lt;p&gt;I got interviewed for my first podcast recently. It’s called HackToStart, and it’s one that I’ve been listening to for a while now, so I was excited to be a guest on the show. Check it out here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.stitcher.com/show/hack-to-start/episode/hack-to-start-episode-49-phil-freo-full-stack-developer-close-io-39389106&quot;&gt;Hack To Start | Phil Freo, Full Stack Developer, Close.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the show we also discuss one of my latest blog posts: &lt;a href=&quot;https://philfreo.com/blog/the-last-20-before-shipping/&quot;&gt;The last 20% before shipping&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Personal</category><category>Startups</category></item><item><title>The last 20% before shipping</title><link>https://philfreo.com/blog/the-last-20-before-shipping/</link><guid isPermaLink="false">https://philfreo.com/?p=668</guid><description>What makes a new feature or product update “done” versus what makes it “really done”? At Close.io we developed our own process to answer this question, based on years of shipping…</description><pubDate>Sat, 23 May 2015 14:05:21 GMT</pubDate><content:encoded>&lt;p&gt;What makes a new feature or product update “done” versus what makes it “really done”? At  &lt;a href=&quot;http://close.io/&quot;&gt;Close.io&lt;/a&gt; we developed our own process to answer this question, based on years of shipping new features for our sales communication platform. Today, we want to share this checklist with you.&lt;/p&gt;
&lt;p&gt;As soon as a new feature you’ve built is running and working on your development server, there’s a strong temptation to think it’s “done”. You want to ship it. After all, the code is working, and you know many of your users would benefit immediately from the change.&lt;/p&gt;
&lt;p&gt;It’s important to stop and ask yourself: What else should we do other than just making a new feature functional? How can I improve the experience for users or make this feature more accessible and maintainable?&lt;/p&gt;
&lt;p&gt;We’ve learned that when you’ve only gotten a feature working, you are at most “80% done” and that the last 20% really makes all the difference. In fact, sometimes the “last 20%” of polishing a feature can take just as long to get right as the “first 80%”, however it also is what separates good from great.&lt;/p&gt;
&lt;p&gt;Here’s our internal checklist that we use before launching significant new features or changes. Whether for launching a new reporting feature or migrating our database to a different cluster, we’ve found this list to be really helpful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is it fast?&lt;/li&gt;
&lt;li&gt;Are the database queries optimized?&lt;/li&gt;
&lt;li&gt;Will it scale for a large number of records?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Code quality&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is the code cleaned up, organized, and properly abstracted?&lt;/li&gt;
&lt;li&gt;Is it well commented / documented? Assume someone else will have to maintain it.&lt;/li&gt;
&lt;li&gt;Are there relevant unit tests?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Edge cases&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do you consider and properly handle edge cases and invalid inputs?&lt;/li&gt;
&lt;li&gt;Did you test the first time experience / no-data-yet case?&lt;/li&gt;
&lt;li&gt;Is it localized? Consider timezones for anything date specific, and unicode.&lt;/li&gt;
&lt;li&gt;Did you test in all the supported browsers / platforms?&lt;/li&gt;
&lt;li&gt;Have you tested for potential security vulnerabilities?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Polish&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does the UI look polished? Is it consistent or better than the rest of your application?&lt;/li&gt;
&lt;li&gt;Does the UI react to various edge cases properly?&lt;/li&gt;
&lt;li&gt;Is any written text as good as it can be? Did you check for typos?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does it need any special deployment process, and is the process well documented? Any data or schema migrations necessary?&lt;/li&gt;
&lt;li&gt;Is everything backwards compatible? If not, did you communicate the change to users?&lt;/li&gt;
&lt;li&gt;Should it be deployed/visible to employees only to dogfood/test for a while?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Communication&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Are the relevant API docs complete?&lt;/li&gt;
&lt;li&gt;Do any FAQ docs need to be updated or written?&lt;/li&gt;
&lt;li&gt;Did you write a blog post announcement, if appropriate?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There you have it. Run through this list before shipping something new and you’re guaranteed to have a better launch.&lt;/p&gt;
&lt;p&gt;Do you have anything to add to the list? Let me know!&lt;/p&gt;
</content:encoded><category>Product</category><category>Programming</category><category>Startups</category><category>Web Development</category></item><item><title>Solve multiple problems at once</title><link>https://philfreo.com/blog/solve-multiple-problems-at-once/</link><guid isPermaLink="false">https://philfreo.com/?p=612</guid><description>Startup engineering teams face many decisions about what to build. At Close.io, many areas compete for the focus of our small engineering team. Customers often have one little…</description><pubDate>Sat, 14 Mar 2015 19:27:13 GMT</pubDate><content:encoded>&lt;p&gt;Startup engineering teams face many decisions about what to build. At &lt;a href=&quot;http://close.io/&quot;&gt;Close.io&lt;/a&gt;, many areas compete for the focus of our small engineering team. Customers often have one little thing they really need. Our team envisions the next big thing to move the product forward. There are poor UX workflows to optimize. We have an idea on how to grow our customer base faster. And of course there are always bugs to fix. The list is endless!&lt;/p&gt;
&lt;p&gt;A small engineering team doesn’t have the time or resources to regularly improve every part of a product. It’s not uncommon for a section of an app, once launched, to remain untouched for a year or longer. We usually work to solve a problem or empower customers in a new way or fix a pain point, and then we move on to something else.&lt;/p&gt;
&lt;p&gt;One reason I believe our super small team at Close.io has been successful is that &lt;strong&gt;we often solve multiple problems at once&lt;/strong&gt;. When a feature needs to be built, we often expand the scope a bit to include other related problems or features that naturally go together with the first one.&lt;/p&gt;
&lt;p&gt;Another way to phrase this idea is: &lt;strong&gt;rather than solving a problem, solve an entire class of problems&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;While this advice may sound obvious, there’s enormous pressure to finish a project as quickly as possible. There’s always the next important feature, bug fix, or redesign from the roadmap to move on to. Shipping even the smallest version of a feature on time can be difficult enough already, since software schedule estimating is never easy. But there’s great value in &lt;em&gt;not&lt;/em&gt; just shipping a feature or fix in the smallest form possible.&lt;/p&gt;
&lt;h3 id=&quot;application-fixing-bugs&quot;&gt;Application: Fixing Bugs&lt;/h3&gt;
&lt;p&gt;Let’s start with a simple real-world application: you notice a bug that needs fixing. After some investigation you figure out what the problem is and how to fix it. You fix it and maybe even add a unit test for this case. Time to move on, right?&lt;/p&gt;
&lt;p&gt;NO! If you stop there, you’re making a crucial mistake.&lt;/p&gt;
&lt;p&gt;At a minimum, you should try to figure out if the same bug exists anywhere else in the codebase. Often a single &lt;a href=&quot;http://beyondgrep.com&quot;&gt;ack&lt;/a&gt; search is enough to find the same bug in many places. Next, consider if there might be other conceptually similar versions of this bug elsewhere. Ideally you’d also follow &lt;a href=&quot;http://www.joelonsoftware.com/items/2008/01/22.html&quot;&gt;The Five Whys&lt;/a&gt; and discover how this bug got introduced, how it got past code review and QA, etc.&lt;/p&gt;
&lt;p&gt;Again, this advice may seem obvious, but consider someone’s natural instinct when a user complains. These complaints usually come in the form of a vague problem (e.g. “it won’t let me change my email address”). First you figure out what the real bug is (e.g. “The form for changing email addresses doesn’t show a confirmation message”). Bugs usually come in very specific forms like this. It’s not uncommon for a programmer to simply fix the bug and move on. But it’s important to stop and consider if similar bugs may exist elsewhere (e.g. “how form confirmation messages work throughout every part of the app”).&lt;/p&gt;
&lt;p&gt;It’s the sign of a mature programmer to ask “why” and consider preventing future bugs of a similar type.&lt;/p&gt;
&lt;h4 id=&quot;example-a-broken-url-on-closeio&quot;&gt;Example: A broken URL on Close.io&lt;/h4&gt;
&lt;p&gt;I recently noticed a problem with a specific URL on our site was not working. I discovered the cause was that two Python view functions in our &lt;a href=&quot;http://flask.pocoo.org&quot;&gt;Flask&lt;/a&gt; app shared the same name, which just silently breaks one of them. My first instinct was to rename the broken view with a unique name, and move on.&lt;/p&gt;
&lt;p&gt;But I remembered it wasn’t the first time this had happened and I recognized it likely wouldn’t be the last, so I thought about the problem more broadly. I knew a syntax issue like this should be detectable, so I spent some time setting up &lt;a href=&quot;http://www.pylint.org&quot;&gt;pylint&lt;/a&gt; and reviewing its results. Pylint uncovered another case of the same error, as well as other types of logical errors elsewhere, which I fixed. Finally, I added pylint to our continuous integration system to automatically detect any Python syntax issues in the future.&lt;/p&gt;
&lt;p&gt;So rather than fixing the broken URL, I fixed all cases where URLs were broken for the same reason. I also found and fixed other unrelated instances of “detectable” syntax issues. And I also automated this process so that this entire class of issues &lt;em&gt;can never happen again&lt;/em&gt;. Do you see how much more powerful this type of fixing can be?&lt;/p&gt;
&lt;p&gt;Once you’ve discovered the specific causes of a bug, there’s no better time to find and fix other similar bugs. Even when the roadmap begs you to move on, the benefits of squashing related problems are even stronger:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It’s good practice to fix bugs before writing other code (#5 in &lt;a href=&quot;http://www.joelonsoftware.com/articles/fog0000000043.html&quot;&gt;The Joel Test&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;If you can discover and fix bugs &lt;em&gt;before&lt;/em&gt; more users experience and report them, you’re preventing user pain.&lt;/li&gt;
&lt;li&gt;You’ve already done the hard part of figuring out the specifics of the problem. If you don’t completely resolve it, you’re forcing a teammate or your future self to have to waste time relearning the same thing!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Don’t just fix bugs. Fix an entire class of bugs.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;applicationdesigning-features&quot;&gt;Application: Designing Features&lt;/h3&gt;
&lt;p&gt;The temptation to solve a single problem at once is even larger when it comes to features. Features are often a response for solving a user’s pain point, or an idea designed to empower your users in a new way. Naturally, the team is excited to ship as soon as possible.&lt;/p&gt;
&lt;p&gt;Furthermore, a good product designer will optimize a feature to be as simple as possible for the specific workflow it’s designed for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The problem is that over time, rather than designing one cohesive experience, you’ve glued a bunch of individual features together&lt;/strong&gt;. If you’re only thinking about solving one specific problem at a time, you’re missing the bigger picture of how everything will fit together.&lt;/p&gt;
&lt;p&gt;Software written in this way turns out to be super complex because hundreds of small problems were solved separately rather than a few big problems being solved elegantly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don’t design a single feature; always be designing for the bigger picture.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;example-1-closeio-search--filtering&quot;&gt;Example 1: Close.io Search &amp;amp; Filtering&lt;/h4&gt;
&lt;p&gt;An example where I think our team nailed this early was with search and filtering. From &lt;a href=&quot;http://elasticsales.com&quot;&gt;ElasticSales&lt;/a&gt; we knew that salespeople would want to slice and dice their leads in a million ways.&lt;/p&gt;
&lt;p&gt;When starting Close.io it would have been understandable if we solved this initially by just slapping a couple of the most commonly requested filter options, like “Lead Status”.&lt;/p&gt;
&lt;p&gt;However we knew that this was a narrow and short-term solution. It wouldn’t be enough to last and wouldn’t be enough to “wow” people. Quickly, power users would outgrow our simple filters and we would be forced to keep adding additional one-off filters and complexity. We’d have to keep redesigning as the number of filters grew and redesigning again for each new idea like exclusion filters or nested “OR” conditions. We would have started fast but slowed very quickly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Instead, we designed a framework to solve the larger problem&lt;/strong&gt;. We &lt;a href=&quot;http://blog.close.io/sales-data-search-writing-a-query-parser-ast-using-part-1&quot;&gt;invented&lt;/a&gt; a &lt;a href=&quot;https://help.close.io/customer/portal/articles/965940-search-overview&quot;&gt;search language&lt;/a&gt; and &lt;a href=&quot;http://blog.close.io/introducing-lead-search&quot;&gt;then UI&lt;/a&gt; to allow filtering by a very large number of useful sales attributes and combine them together with boolean and/or/not keywords. It took longer to do it this way than just adding a couple basic filters. &lt;em&gt;But we established a paradigm of how searching and filtering worked in Close.io that has powered innumerable use cases our customers needed and has lasted 2+ years.&lt;/em&gt; Our customers rave about its power, and &lt;a href=&quot;https://web.archive.org/web/20181209214904/http://pando.com/2013/06/26/close-io-adds-smartsearch-data-analytics-so-simple-a-salesperson-can-do-it/&quot;&gt;PandoDaily wrote about it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’m definitely not saying we had to build this feature to 100% completion from day 1 (and we didn’t – we still iterate on it today – and in many ways it’s &lt;em&gt;very&lt;/em&gt; far from complete). But thinking through a scalable solution for this problem rather than slapping on a few quick filters has given us a big advantage. Having an end goal in mind allowed us build a version 1 that didn’t have to be thrown away when we built v2 and v3. We have ideas for what an amazing version 5 and 10 may look like, and we won’t have to start over – all because we planned ahead to solve search &amp;amp; filtering more broadly.&lt;/p&gt;
&lt;h4 id=&quot;example-2-closeio-reporting&quot;&gt;Example 2: Close.io Reporting&lt;/h4&gt;
&lt;p&gt;Some of our competitors have dozens of individual “reports”. They tack on a new report every few weeks because users always want more reporting. Close.io was really far behind in reporting but the thought of adding dozens of reports made us want to cry. So instead we built one super powerful charting tool (&lt;a href=&quot;http://blog.close.io/introducing-explorer-customizable-reporting-graphs&quot;&gt;Explorer&lt;/a&gt;) that, in one fell swoop, allows you to visualize almost any attribute of your teams’s sales activity.&lt;/p&gt;
&lt;h4 id=&quot;example-3-closeio-bulk-actions&quot;&gt;Example 3: Close.io Bulk Actions&lt;/h4&gt;
&lt;p&gt;We needed to build a way for users to “bulk delete” all their leads. Rather than building this alone, we designed a system that would work for not only Bulk Delete but also Bulk Edit and Bulk Email (two other features we knew we wanted to build). Because of designing for this, we were able to later launch the additional two features within a very short period of time. Coding the two additional features became much simpler and the UX for all bulk actions was considered together rather than tacked on without cohesion.&lt;/p&gt;
&lt;h4 id=&quot;architect-your-product-to-solve-an-entire-class-of-problems-at-once&quot;&gt;Architect your product to solve an entire class of problems at once.&lt;/h4&gt;
&lt;p&gt;If you don’t, you’ll end up with software that’s missing important features and users will quickly outgrow the one thing you helped them with. Or you’ll keep tacking on additions in a non-cohesive way which makes a complex product over time.&lt;/p&gt;
&lt;p&gt;Said another way: it’s easier to end up with &lt;em&gt;both&lt;/em&gt; successful users &lt;em&gt;and&lt;/em&gt; a cohesive UI &amp;amp; UX if you solve and design for a few big problems rather than a bunch of individual little ones.&lt;/p&gt;
&lt;h3 id=&quot;application-refactors&quot;&gt;Application: Refactors&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Technical_debt&quot;&gt;Technical debt&lt;/a&gt; can clearly become a big problem and slow development. But it almost never feels worth rewriting something just for the sake of code quality. The benefit of doing projects solely to “pay back” technical debt is hard to justify.&lt;/p&gt;
&lt;p&gt;The best time to solve technical debt, refactor code, etc. is in the midst of making other changes to that part of the system. When you’re working on an improvement involving a problematic part of the codebase and you’re considering making bad code even worse… go ahead and take the extra time to refactor and improve it. There’s no better time to do so, since you’re already having to grok how it works and carefully test those parts related to your improvement.&lt;/p&gt;
&lt;h3 id=&quot;application-redesigns&quot;&gt;Application: Redesigns&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;When redesigning how one part of your product works, consider how the rest of your product works.&lt;/strong&gt; It may be easier to solve multiple problems that relate to each other all at once.&lt;/p&gt;
&lt;h4 id=&quot;examplecloseio-onboarding-process--email-setup&quot;&gt;Example: Close.io Onboarding Process &amp;amp; Email Setup&lt;/h4&gt;
&lt;p&gt;We wanted to introduce a set of onboarding steps for new Close.io users. One step would be an easier way to connect your email account (for our &lt;a href=&quot;https://help.close.io/customer/portal/articles/964839-2-way-email-integration&quot;&gt;2-way email syncing&lt;/a&gt; to work) rather than having users do so later in Settings. What we did is build and launch a few features all at once:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Onboarding steps for new users&lt;/li&gt;
&lt;li&gt;Simplify getting email account credentials by:
&lt;ul&gt;
&lt;li&gt;Auto-detecting your email service, IMAP/SMTP hostname, port, etc. when possible&lt;/li&gt;
&lt;li&gt;Consolidating setup of incoming &amp;amp; outgoing email settings into one step&lt;/li&gt;
&lt;li&gt;Use OAuth instead of passwords when a Gmail / Google Apps account is detected&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Support for multiple email accounts &amp;amp; identities per user&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not all of these features were crucial for the main priority at the time, which was to improve onboarding and make it easier to setup email. But they made a lot of sense to build together, since they were interrelated. We would have had to redesign, recode, and retest the Email Settings page regardless so it was the perfect time to design it to support setting up multiple accounts.&lt;/p&gt;
&lt;p&gt;Supporting multiple accounts is a valuable feature that we always planned on building. But if we hadn’t built it alongside these other features, it likely wouldn’t have become a big enough priority to get built on its own for quite some time. By building to solve multiple problems at once, we were able to do more, faster, than had we been trying to solve independent problems in serial.&lt;/p&gt;
&lt;h3 id=&quot;risks--rewards&quot;&gt;Risks &amp;amp; Rewards&lt;/h3&gt;
&lt;p&gt;You may now be thinking, “Isn’t this scope creep, and isn’t scope creep a bad thing?”&lt;/p&gt;
&lt;p&gt;Indeed, if you keep expanding the scope of your projects to solve more and more problems you will never ship or meet deadlines.&lt;/p&gt;
&lt;p&gt;But I’m actually advocating for more planning. More deliberateness in your design decisions and planning. More hesitation before starting projects that only solve only one problem. Design your product with end goals in mind. Design code and processes with your future team in mind.&lt;/p&gt;
&lt;p&gt;Expand a project scope opportunistically where it makes sense. Reschedule items onto the roadmap sooner if they are easier to build alongside whatever your current priority is. Often you won’t return to a problem for many months or even years. So if you can put an entire set of problems to rest all at once, do it even if it takes a bit longer.&lt;/p&gt;
&lt;p&gt;The principles I’ve been talking about should help you make a much better product over time. When you solve one problem, it’s not that much harder to solve a bigger class of the problem.&lt;/p&gt;
&lt;p&gt;The way to keep from turning this advice into scope creep is to slow down. Not slow down in the sense that your team &amp;amp; product shouldn’t be moving quickly. But slow down in the sense that you should do less, but better. Do fewer things, but more that have longterm impact. You can’t do this for everything, but try to do it for the important parts.&lt;/p&gt;
&lt;p&gt;So the next time you design a feature, fix a bug, or otherwise try to improve your product, ask yourself, “Can I solve multiple problems at once?”&lt;/p&gt;
</content:encoded><category>Entrepreneurship</category><category>Product</category><category>Web Development</category></item><item><title>My mobile workstation setup</title><link>https://philfreo.com/blog/my-mobile-workstation-setup/</link><guid isPermaLink="false">https://philfreo.com/?p=623</guid><description>Since my wife got a nursing job in Stockton, I’ve drastically increased the time I spend working while traveling, in coffee shops or hotels where I don’t have my typical desk and…</description><pubDate>Wed, 21 Jan 2015 17:48:10 GMT</pubDate><content:encoded>&lt;p&gt;Since my wife got a nursing job in Stockton, I’ve drastically increased the time I spend working while traveling, in coffee shops or hotels where I don’t have my typical desk and monitor setup. Since I get a &lt;em&gt;ton&lt;/em&gt; of compliments and questions about it when I’m working from Starbucks all day, I thought I’d share my ultimate mobile workstation guide.&lt;/p&gt;
&lt;p&gt;My main requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allow me to get a lot of work done, be comfortable, and have good posture. Replicating my in-office experience as much as possible.&lt;/li&gt;
&lt;li&gt;Easy to transport in my backpack&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here goes!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Laptop Stand:&lt;/strong&gt; Why? Because hunching over a laptop screen is terrible for you. The trick is finding something that’s portable and comes as close to replacing a monitor as possible.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Best option: &lt;a href=&quot;http://www.amazon.com/Roost-Laptop-Stand-Productivity-Lightweight/dp/B01C9KG8IG/ref=as_li_ss_tl?ie=UTF8&amp;amp;qid=1458315904&amp;amp;sr=8-50&amp;amp;keywords=laptop+stand&amp;amp;linkCode=ll1&amp;amp;tag=philfrcom-20&amp;amp;linkId=1392d41ea47bb90128d9134d2308926e&quot;&gt;The Roost Stand&lt;/a&gt;. (~$75). It folds up &lt;em&gt;extremely&lt;/em&gt; small and is incredibly lightweight. It also has a really great elevation height – higher than any other stand I’ve seen (but not too tall). Definitely worth the money if you work on the road a lot. It has a unique way to hold the laptop firmly.&lt;/li&gt;
&lt;li&gt;Cheaper option: &lt;a href=&quot;http://www.amazon.com/gp/product/B0044RUSZM/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B0044RUSZM&amp;amp;linkCode=as2&amp;amp;tag=philfrcom-20&amp;amp;linkId=EY4PDRB74GZNRPZ2&quot;&gt;Griffin Technology GC16034 Elevator Laptop Stand&lt;/a&gt; (~$32). I used this at first and it works well and is collapsable into 3 pieces. Compared to The Roost, this stand does make it faster to quickly lift your laptop from it since it’s a simple platform. But it does take up a fair amount of backpack space and isn’t super lightweight, nor is the height quite as tall as I would have liked. So for me, The Roost was a great upgrade.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Keyboard:&lt;/strong&gt; I use and am very happy with &lt;a href=&quot;http://www.amazon.com/Apple-Wireless-Keyboard-MC184LL-B/dp/B005DLDO4U?tag=philfrcom-20&quot;&gt;Apple’s Bluetooth Keyboard&lt;/a&gt; (~$64). Looks nice, works great once it’s paired once, and is very slim. There are also &lt;a href=&quot;http://www.amazon.com/gp/product/B005ONMDYE/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B005ONMDYE&amp;amp;linkCode=as2&amp;amp;tag=philfrcom-20&amp;amp;linkId=OYPLSH4LIWH43ZC5&quot;&gt;cheaper&lt;/a&gt; &lt;a href=&quot;http://www.amazon.com/s/?_encoding=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;fst=as%3Aoff&amp;amp;keywords=bluetooth%20keyboard&amp;amp;linkCode=ur2&amp;amp;qid=1421859912&amp;amp;rh=n%3A172282%2Cn%3A541966%2Cn%3A172456%2Cn%3A12879431%2Ck%3Abluetooth%20keyboard%2Cp_72%3A1248879011&amp;amp;rnid=1248877011&amp;amp;tag=philfrcom-20&amp;amp;linkId=CP7KANXXNHNJWKOT&quot;&gt;options&lt;/a&gt; that probably work just as well. Just be sure to get a small one (no number keypad) so it doesn’t take up much backpack room. At my office and home desk I like USB keyboards, but while traveling having fewer wires (via Bluetooth) is a big plus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mouse:&lt;/strong&gt; At work, at home, or while traveling I always use a &lt;a href=&quot;http://www.amazon.com/gp/product/B002HWRJBM/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B002HWRJBM&amp;amp;linkCode=as2&amp;amp;tag=philfrcom-20&amp;amp;linkId=SI4U7LPNNHJM4GSX&quot;&gt;Logitech Wireless Performance Mouse MX for PC and Mac&lt;/a&gt; (~$60). I’ve tried a lot of wireless mice and have happily been using this one for the last couple years. It does have use a tiny Logitech Unifying USB wireless receiver (I had lagging/performance issues with Bluetooth mice).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Headphones:&lt;/strong&gt; While any headphones can work and simple iPhone earbuds are the most portable, I ultimately got &lt;a href=&quot;http://www.amazon.com/gp/product/B00KGGK71A/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B00KGGK71A&amp;amp;linkCode=as2&amp;amp;tag=philfrcom-20&amp;amp;linkId=WEKAJKJGMCKZDH4C&quot;&gt;Samsung LEVEL Noise Cancelling Wireless Headphones&lt;/a&gt;. They aren’t cheap (full price ~$300, though you can often find them cheaper) but I got them for a few reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Bluetooth&lt;/em&gt;. Previously I used simple wired &lt;a href=&quot;http://www.amazon.com/gp/product/B000065BPB/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B000065BPB&amp;amp;linkCode=as2&amp;amp;tag=philfrcom-20&amp;amp;linkId=W7XCHWXNL477ONMU&quot;&gt;Sennheiser HD-280 PRO Headphones&lt;/a&gt; ($87) but mine were falling apart and I liked the idea of not having a big coiled wire hanging off my Starbucks table or getting tangled in my airline seat and being able to run to the bathroom at home without stopping whatever I’m listening to.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Noise canceling.&lt;/em&gt; Actively blocks out background noise from the coffee shop, though this is especially nice for use on airplanes.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Portable.&lt;/em&gt; They come with a nice semi-hard case. It takes up a good bit of room but is nice and protects them from getting ripped in my backpack.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Sound quality.&lt;/em&gt; &lt;a href=&quot;http://thewirecutter.com/reviews/best-noise-cancelling-headphones/&quot;&gt;The Wirecutter recommends&lt;/a&gt; the &lt;a href=&quot;http://www.amazon.com/gp/product/B00M1NEUKK/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B00M1NEUKK&amp;amp;linkCode=as2&amp;amp;tag=philfrcom-20&amp;amp;linkId=N6OOWESJSSRLFDRQ&quot;&gt;Bose QuietComfort 25 Headphones&lt;/a&gt; as the best Over-Ear Noise Canceling headphones. But if you do want Bluetooth, my &lt;a href=&quot;http://www.amazon.com/gp/product/B00KGGK71A/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B00KGGK71A&amp;amp;linkCode=as2&amp;amp;tag=philfrcom-20&amp;amp;linkId=WEKAJKJGMCKZDH4C&quot;&gt;Samsung LEVEL&lt;/a&gt; choice is their top pick with Bluetooth.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Standing Desk:&lt;/strong&gt; It’s good to mix sitting and standing throughout your day. At home I made a &lt;a href=&quot;https://philfreo.com/blog/my-ikea-standing-desk/&quot;&gt;cheap standing desk&lt;/a&gt;. At work we’ve got &lt;a href=&quot;http://store.steelcase.com/products/series-7/&quot;&gt;Steelcase Series 7 sit/stand desks&lt;/a&gt;. But these options won’t work while traveling. Fortunately I’ve noticed that almost every Starbucks I’ve visited has some hightop tables that happen to be at the &lt;em&gt;perfect&lt;/em&gt; height for me to stand at. I’ll just alternative every hour or so between standing and sitting. In hotel rooms, I’ve gotten creative with putting my laptop (or laptop stand) on top of various furniture if I want to stand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Internet:&lt;/strong&gt; Finding solid internet is crucial. In Stockton, I’ll only work from Starbucks with Wifi powered by Google because those are way faster than the rest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Power:&lt;/strong&gt; Whether it’s at an airport, coffee shop, or conference, there’s one little trick that allows me to almost always have access to power outlets, even if it looks like they are all full. I simply keep this little &lt;a href=&quot;http://www.amazon.com/gp/product/B000P9SXTG/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B000P9SXTG&amp;amp;linkCode=as2&amp;amp;tag=philfrcom-20&amp;amp;linkId=CN6IFK6PW6IEL65T&quot;&gt;power outlet splitter cube&lt;/a&gt; in my backpack. Then even if an outlet is full, I can plug in to create an extra outlet for myself, the original plug, plus an extra free slot.&lt;/p&gt;
&lt;div class=&quot;wp-img&quot; style=&quot;max-width: 150px&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;313yx8V1GeL&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;500&quot; height=&quot;441&quot; src=&quot;https://philfreo.com/_astro/313yx8v1gel.DqTy-zcJ_13ImDr.webp&quot; srcset=&quot;https://philfreo.com/_astro/313yx8v1gel.DqTy-zcJ_13ImDr.webp 500w&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Here it is…&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://philfreo.com/wp-content/uploads/2015/01/FullSizeRender-2.jpg&quot;&gt;&lt;img alt=&quot;&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2446&quot; height=&quot;3264&quot; src=&quot;https://philfreo.com/_astro/fullsizerender-2.8ifT-xMm_Z2uL70c.webp&quot; srcset=&quot;https://philfreo.com/_astro/fullsizerender-2.8ifT-xMm_ksD2P.webp 640w, https://philfreo.com/_astro/fullsizerender-2.8ifT-xMm_Z1Cyx6D.webp 750w, https://philfreo.com/_astro/fullsizerender-2.8ifT-xMm_syI0A.webp 828w, https://philfreo.com/_astro/fullsizerender-2.8ifT-xMm_Zo69Vy.webp 1080w, https://philfreo.com/_astro/fullsizerender-2.8ifT-xMm_1J7RQE.webp 1280w, https://philfreo.com/_astro/fullsizerender-2.8ifT-xMm_Zdo3Ae.webp 1668w, https://philfreo.com/_astro/fullsizerender-2.8ifT-xMm_2irsXa.webp 2048w, https://philfreo.com/_astro/fullsizerender-2.8ifT-xMm_Z2uL70c.webp 2446w&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>Personal</category><category>Stuff</category></item><item><title>How one of my tweets landed Close.io a $585/month customer</title><link>https://philfreo.com/blog/how-one-of-my-tweets-landed-close-io-a-585month-customer/</link><guid isPermaLink="false">https://philfreo.com/?p=617</guid><description>One of my tweets is featured in an article on Mention.com: How Close.io Closed $585 in Monthly Revenue from 1 Tweet. Check it out!</description><pubDate>Tue, 09 Dec 2014 16:52:18 GMT</pubDate><content:encoded>&lt;p&gt;One of my tweets is featured in an article on Mention.com:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://blog.mention.com/close-io-social-listening/&quot;&gt;How Close.io Closed $585 in Monthly Revenue from 1 Tweet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Check it out!&lt;/p&gt;
</content:encoded><category>Close.io</category><category>Entrepreneurship</category><category>Product</category><category>Startups</category><category>Uncategorized</category></item><item><title>Launched: ArticleListen.com – integration between Pocket &amp; Umano to hear your saved articles read aloud</title><link>https://philfreo.com/blog/articlelisten-pocket-umano-integration/</link><guid isPermaLink="false">https://philfreo.com/?p=607</guid><description>I built a quick mashup/integration between two services that I love using, that I thought would be “better together”. It’s live at ArticleListen.com. I use Pocket to save a queue…</description><pubDate>Sat, 11 Oct 2014 22:37:07 GMT</pubDate><content:encoded>&lt;p&gt;I built a quick mashup/integration between two services that I love using, that I thought would be “better together”. It’s live at &lt;a href=&quot;http://articlelisten.com&quot;&gt;ArticleListen.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://philfreo.com/wp-content/uploads/2014/10/Screenshot-2015-08-21-07.07.34.png&quot;&gt;&lt;img alt=&quot;&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2674&quot; height=&quot;1966&quot; src=&quot;https://philfreo.com/_astro/screenshot-2015-08-21-07.07.34.BMoFmcHG_Ztg40D.webp&quot; srcset=&quot;https://philfreo.com/_astro/screenshot-2015-08-21-07.07.34.BMoFmcHG_Z2kFDsH.webp 640w, https://philfreo.com/_astro/screenshot-2015-08-21-07.07.34.BMoFmcHG_2f7j5q.webp 750w, https://philfreo.com/_astro/screenshot-2015-08-21-07.07.34.BMoFmcHG_Z19BL86.webp 828w, https://philfreo.com/_astro/screenshot-2015-08-21-07.07.34.BMoFmcHG_Z20ezhA.webp 1080w, https://philfreo.com/_astro/screenshot-2015-08-21-07.07.34.BMoFmcHG_Z1jUoyk.webp 1280w, https://philfreo.com/_astro/screenshot-2015-08-21-07.07.34.BMoFmcHG_Z2h1960.webp 1668w, https://philfreo.com/_astro/screenshot-2015-08-21-07.07.34.BMoFmcHG_Z7NXoi.webp 2048w, https://philfreo.com/_astro/screenshot-2015-08-21-07.07.34.BMoFmcHG_xHAvI.webp 2560w, https://philfreo.com/_astro/screenshot-2015-08-21-07.07.34.BMoFmcHG_Ztg40D.webp 2674w&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I use &lt;a href=&quot;http://getpocket.com&quot;&gt;Pocket&lt;/a&gt; to save a queue of any article (blog post, news story, etc.) that I see on Hacker News, Twitter, Facebook, etc. There’s a web &amp;amp; mobile browser extensions for saving articles you see, and Mac &amp;amp; iOS apps for reading these articles later all in one place, even offline.&lt;/p&gt;
&lt;p&gt;Using Pocket can be good for productivity because you may see a headline that interests you, but you really shouldn’t be reading it now. By saving it to Pocket you know you can easily find and read it later. I’ve got a few hundred articles saved from over the past couple years. I often read a few articles at a time, but they still build up.&lt;/p&gt;
&lt;p&gt;I also use &lt;a href=&quot;https://umano.me&quot;&gt;Umano&lt;/a&gt;, which is a service that has professional voice actors record narrated articles from across the web. It’s perfect for commuting. Many of the articles are only a few minutes long, so even a quick trip to the store or walking to the train can be enough to hear an article or two about something that interests you. Tech, current news, etc.&lt;/p&gt;
&lt;p&gt;Umano has some article discovery built in (a “popular” page and a way to see which articles your Facebook friends on Umano liked), however they have thousands of articles already recorded and so finding what you really want to listen to is still difficult.&lt;/p&gt;
&lt;p&gt;Then I realized that all the articles I wanted to read or listen to were already saved in my Pocket queue, so during part of a couple weekends I put together this site to do a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Look through my Pocket queue and identify which articles were already on Unman&lt;/li&gt;
&lt;li&gt;Allow me to very easily add matching articles to my “playlist” in Umano&lt;/li&gt;
&lt;li&gt;Also allow me to “Vote” in Umano for the articles that are already in my queue&lt;/li&gt;
&lt;li&gt;Add “tags” into Pocket for articles that were added to my Umano playlist, so I can easily archive those in Pocket&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Umano does have a Chrome extension (and generic bookmarklet) for “voting” for articles, but I wanted my primary queue to be something like Pocket, which is better built for general purpose article saving, I didn’t want to have to save every article to two places, and I mostly wanted to listen to the articles that were &lt;em&gt;already&lt;/em&gt; in my queue.&lt;/p&gt;
&lt;p&gt;Check it out: &lt;a href=&quot;http://articlelisten.com&quot;&gt;ArticleListen.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Feedback is welcome.&lt;/p&gt;
&lt;p&gt;Built using Python/Flask and Bootstrap.&lt;/p&gt;
&lt;p&gt;Future improvements I’d like to make: It should continually monitor a Pocket queue for new articles ongoing – looking for matches and adding them to Umano without any user interaction. At the moment you just have to login to the site occasionally.&lt;/p&gt;
</content:encoded><category>Personal</category><category>Uncategorized</category></item><item><title>The Elements of User Onboarding</title><link>https://philfreo.com/blog/the-elements-of-user-onboarding/</link><guid isPermaLink="false">https://philfreo.com/?p=592</guid><description>I just finished reading “The Elements of User Onboarding” by Samuel Hulick. I discovered the book through the excellent site UserOnboard.com which has chronicled every screen in…</description><pubDate>Mon, 04 Aug 2014 00:58:22 GMT</pubDate><content:encoded>&lt;p&gt;I just finished reading “The Elements of User Onboarding” by &lt;a href=&quot;http://samuelhulick.com/&quot;&gt;Samuel Hulick&lt;/a&gt;. I discovered the book through the excellent site &lt;a href=&quot;http://www.useronboard.com/&quot;&gt;UserOnboard.com&lt;/a&gt; which has chronicled every screen in the onboarding process of several web and mobile apps and added great commentary (positive &amp;amp; negative) on each screen.&lt;/p&gt;
&lt;p&gt;If you help make a web/desktop/mobile app, I highly recommend going through a few of these teardowns, as they alone will make you re-think some parts of your onboarding process. These teardowns together with the book have given me lots of great ideas for drastically improving the liklihood that a new user of &lt;a href=&quot;http://close.io/&quot;&gt;Close.io&lt;/a&gt; will become successful.&lt;/p&gt;
&lt;p&gt;For each chapter, I wrote down a brief summary or simply something that stood out to me. By all means, this isn’t a replacement for &lt;a href=&quot;https://www.useronboard.com/training/&quot;&gt;getting the book&lt;/a&gt; – but a teaser and reminder to myself what I read!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 1&lt;/strong&gt;&lt;br&gt;
Onboarding starts way back with your marketing / how users hear about you and needs to be consistent throughout their &lt;em&gt;entire&lt;/em&gt; experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 2-3&lt;/strong&gt;&lt;br&gt;
Show people how you will make &lt;em&gt;them&lt;/em&gt; better&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 4&lt;/strong&gt;&lt;br&gt;
Help people see specific details of how your app would improve their life (“aha moment”)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 5&lt;/strong&gt;&lt;br&gt;
Cater to their emotions – show you understand the struggle of their current methods (use the exact wording of a similar audience expressing their pain points) + help them emotionally connect with your superior way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 6&lt;/strong&gt;&lt;br&gt;
Your app has a personality. What is it?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 7&lt;/strong&gt;&lt;br&gt;
Give them logic to help defend emotional connection. Focus on how your product is as good as competitors + exceeds all other options. Also pitch customer service, resources, tech support, humans, add trust via testimonials (face/name/title/logo), and numerical social proof. state that you serve “stars” and “aspirational stars”.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 8&lt;/strong&gt;&lt;br&gt;
Clear all possible obstacles in the path. Overcome objections, remove fiction, &amp;amp; remove points of disconnect (checking email for confirmation, looking up coupon codes, etc)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 9&lt;/strong&gt;&lt;br&gt;
Design an experience that’s attainable in their first usage that gives that some joy / quick win / at least a glimpse to help them believe your product will be able to help them&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 10&lt;/strong&gt;&lt;br&gt;
Cut down the first run experiences to the fewest ones strictly necessary for helping them achieve a goal&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 11&lt;/strong&gt;&lt;br&gt;
First page after signup matters. Don’t keep users in a limbo state too long&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 12&lt;/strong&gt;&lt;br&gt;
Imagine you were in-person standing behind a new user helping them become successful with your app. Design your onboarding to be like this. Adding more UI to explain your UI is not onboarding. Make your UI better rather than adding explanations. Rather than fake sample content, use that default content to explain your app / give them a walkthrough, if possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 13&lt;/strong&gt;&lt;br&gt;
Give users steps to complete in a way that motivates them to accomplish them. Show that progress has already been made even from the beginning. Make them happy by congratulating/rewarding upon first difficult or important action accomplished (Close.io: we should congratulate them when they close a deal or get their leads imported).&lt;/p&gt;
&lt;p&gt;Quora has a nice “quest” that stays open as they browse around, giving them things to do on their own rather than forcing them through a mandatory guide&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ch 14&lt;/strong&gt;&lt;br&gt;
Send an email “congrats – your making progress!” after they do something good, that serves as a catalyst for next action.&lt;/p&gt;
</content:encoded><category>Onboarding</category><category>Startups</category><category>Uncategorized</category><category>UX</category></item><item><title>My interview on Sales4Startups about Close.io</title><link>https://philfreo.com/blog/my-interview-on-sales4startups-about-close-io/</link><guid isPermaLink="false">https://philfreo.com/?p=583</guid><description>Sales 4 Startups published an interview with me about Close.io. Interview: Phil Freo, Engineering Lead @Close.io Sales4StartUps: Tell us about the Close.io story. How was it…</description><pubDate>Tue, 15 Apr 2014 18:33:53 GMT</pubDate><content:encoded>&lt;p&gt;Sales 4 Startups &lt;a href=&quot;http://sales4startups.org/interview-phil-freo-of-close-io/&quot;&gt;published an interview with me about Close.io&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h1 id=&quot;interview-phil-freo-engineering-lead-closeio&quot;&gt;Interview: Phil Freo, Engineering Lead @Close.io&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Sales4StartUps&lt;/strong&gt;: Tell us about the Close.io story. How was it started? What problem were you trying to solve?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Phil:&lt;/strong&gt; Close.io was developed as an internal product to make our own salespeople at ElasticSales more efficient. Our company was doing sales for a bunch of companies (with different types of sales processes and needs) and through all these difference sales campaigns we recognized a lack of good software geared at the day-to-day tasks a salesperson faces. Our team needed to be able to make many phone calls quickly, send more emails, and see a history of sales activity for any given lead without having to do a bunch of manual data entry, and surprisingly none of the solutions we tried seem to actually be designed to make salespeople’s lives better in these ways.&lt;/p&gt;
&lt;p&gt;After several of our customers asked if their salespeople could use our software (since they saw our productivity improvements from it), we decided to focus on productizing it, and Close.io was born.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://sales4startups.org/interview-phil-freo-of-close-io/&quot;&gt;Read the entire post&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Uncategorized</category></item><item><title>Coinbase Bug Bounty Award</title><link>https://philfreo.com/blog/coinbase-bug-bounty-award/</link><guid isPermaLink="false">https://philfreo.com/?p=577</guid><description>Coinbase is one of the best Bitcoin related sites/services. They’re certainly the easiest way to buy or sell Bitcoin if you have a U.S. bank account. When I saw their Bug Bounty…</description><pubDate>Sun, 16 Mar 2014 01:47:59 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://coinbase.com/?r=4fedfb2d6cbd3b0003000845&amp;amp;utm_campaign=user-referral&amp;amp;src=referral-link&quot;&gt;Coinbase&lt;/a&gt; is one of the best Bitcoin related sites/services. They’re certainly the easiest way to buy or sell Bitcoin if you have a U.S. bank account. When I saw their &lt;a href=&quot;https://coinbase.com/whitehat&quot;&gt;Bug Bounty program&lt;/a&gt; where they offer $1,000 USD worth of Bitcoin if you find a security vulnerability, it improved my trust in their security, but of course also made me want to look for security bugs…&lt;/p&gt;
&lt;p&gt;I had recently heard about &lt;a href=&quot;http://portswigger.net/burp/&quot;&gt;Burp&lt;/a&gt; and wanted to play around with it. When I had some spare time around Thanksgiving, I fired it up and started poking at all the forms in the various Coinbase settings pages, trying to find an XSS or other security issue. I pretty quickly managed to generate a few 500 errors, but that wouldn’t be enough. But within a couple hours I found what I considered to be a real XSS issue. It wasn’t a huge issue, and not something a large number of users would have been affected by, but could result in an XSS nevertheless.&lt;/p&gt;
&lt;p&gt;It took a while for them to get back to me, but eventually they agreed it was an XSS and granted me the $1000 worth of BTC and added me to their &lt;a href=&quot;https://coinbase.com/whitehat&quot;&gt;Awards&lt;/a&gt; section!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://cl.ly/image/0G010J2P0o1n/Screen%20Shot%202014-03-15%20at%206.25.56%20PM.png&quot; alt=&quot;&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot;&gt;&lt;/p&gt;
</content:encoded><category>Bitcoin</category><category>Security</category><category>Web Development</category></item><item><title>My IKEA standing desk</title><link>https://philfreo.com/blog/my-ikea-standing-desk/</link><guid isPermaLink="false">https://philfreo.com/?p=565</guid><description>I’ve been wanting a standing desk at home, ever since our old Elastic office had standing desks. The key though is getting it to be the perfect height, and not spending a fortune…</description><pubDate>Mon, 23 Dec 2013 01:24:31 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been wanting a standing desk at home, ever since our old Elastic office had standing desks. The key though is getting it to be the perfect height, and not spending a fortune. After some careful measurements I determined that my standing desk surface should be exactly 40″ from the ground, so that my arms at ~90 degree angles would fall perfectly on them when I’m standing (I’m 5’6″ or 5’7″).&lt;/p&gt;
&lt;p&gt;Luckily I had an existing &lt;a href=&quot;http://www.ikea.com/us/en/catalog/products/S09806763/&quot;&gt;GALANT Ikea adjustable desk&lt;/a&gt; ($150 new), which can get pretty tall on its own (almost 35.5″). So the trick was to find something sturdy to stick the legs on that was at least 4.5″ tall. Turns out &lt;a href=&quot;http://www.amazon.com/Risers-Furniture-Create-Underbed-Storage/dp/B003UHYTZ2/ref=sr_1_2?ie=UTF8&amp;amp;tag=frquor-20&quot;&gt;these $10 bed risers&lt;/a&gt; will do the job perfectly! Bed risers come in various heights and sizes but these are perfect since they raise the desk 5.25″ and the insets are big enough for the GALANT desk legs to fit.&lt;/p&gt;
&lt;p&gt;So, if you’re looking for a cheap standing desk setup, I’d recommend the GALANT from Ikea + these bed risers, and you can have a simple standing desk up to about 41″ tall.&lt;/p&gt;
&lt;p&gt;You likely need to raise your monitor up so that it’s at the correct eye level when your head is straight. I’m using books but they also have &lt;a href=&quot;http://www.amazon.com/3M-Adjustable-Monitor-Stand-MS80B/dp/B0006HVM5E/?ie=UTF8&amp;amp;tag=frquor-20&quot;&gt;monitor stands&lt;/a&gt; that let you put stuff underneath.&lt;/p&gt;
&lt;p&gt;I also recommend a laptop stand like this &lt;a href=&quot;http://www.amazon.com/Griffin-Technology-GC16034-Elevator-Laptop/dp/B0044RUSZM/?ie=UTF8&amp;amp;tag=frquor-20&quot;&gt;$33 Griffin Elevator Laptop Stand&lt;/a&gt; to raise your laptop (as a second monitor) to around the same height.&lt;/p&gt;
&lt;p&gt;It’s a bit messy… but here’s the final product:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://f.cl.ly/items/0r2u342I2K3D3t0p2Y2Z/2013-12-22%2017.17.42.jpg&quot; alt=&quot;My Ikea Standing Desk&quot; sizes=&quot;(min-width: 690px) 650px, calc(100vw - 32px)&quot;&gt;&lt;/p&gt;
</content:encoded><category>Personal</category><category>Productivity</category></item><item><title>How to view a color diff of text from two PDFs</title><link>https://philfreo.com/blog/how-to-view-a-color-diff-of-text-from-two-pdfs/</link><guid isPermaLink="false">https://philfreo.com/?p=557</guid><description>I recently had to look at two different legal documents (PDFs) that were mostly the same, but I wanted to spot any differences. My first attempt was to install the cross-platform…</description><pubDate>Sat, 28 Sep 2013 02:09:10 GMT</pubDate><content:encoded>&lt;p&gt;I recently had to look at two different legal documents (PDFs) that were mostly the same, but I wanted to spot any differences.&lt;/p&gt;
&lt;p&gt;My first attempt was to install the cross-platform GUI tool &lt;a href=&quot;http://www.qtrac.eu/diffpdf.html&quot;&gt;DiffPDF&lt;/a&gt;. This mostly worked but was annoying because it seemed to basically only diff &lt;em&gt;within a page&lt;/em&gt; – so if your paragraph bumped to another page in the new version of the document, you couldn’t really spot changes.&lt;/p&gt;
&lt;p&gt;Then I installed &lt;strong&gt;pdftotext&lt;/strong&gt; with macports: “sudo port install poppler”. From there you can diff the two text files, like normal.&lt;/p&gt;
&lt;p&gt;However the format was still not ideal in the standard FileMerge, “diff” or “vimdiff” programs (even with ignoring whitespace) because it would show an entire paragraph changing even if just one word did. And it would also show issues in all the places where text wrapping happened on. different words&lt;/p&gt;
&lt;p&gt;So finally I found a couple other useful tools, &lt;a href=&quot;https://www.gnu.org/software/wdiff/&quot;&gt;wdiff&lt;/a&gt; (for word-by-word rather than line-by-line comparisons) and &lt;a href=&quot;http://www.colordiff.org/&quot;&gt;colordiff&lt;/a&gt; to colorize the output. Installed with: sudo port install wdiff colordiff. And threw in a &lt;a href=&quot;http://stackoverflow.com/a/345536/137067&quot;&gt;trick&lt;/a&gt; to avoid needing the .txt temp files.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ wdiff &amp;lt;(pdftotext old.pdf -) &amp;lt;(pdftotext new.pdf -) | colordiff&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Beautiful! Now I can easily spot the specific words, nicely colored, that have changed.&lt;/p&gt;
&lt;p&gt;Only thing I would improve is for it to print only the lines with changes (plus 3 context lines above/below), rather than all lines.&lt;/p&gt;
</content:encoded><category>OS X</category></item></channel></rss>