<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Rado's Blog]]></title><description><![CDATA[Rado's Blog]]></description><link>https://blog.rstankov.com/</link><image><url>http://blog.rstankov.com/favicon.png</url><title>Rado&apos;s Blog</title><link>https://blog.rstankov.com/</link></image><generator>Ghost 1.8</generator><lastBuildDate>Mon, 20 Apr 2026 12:43:59 GMT</lastBuildDate><atom:link href="https://blog.rstankov.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[I started a newsletter]]></title><description><![CDATA[<div class="kg-card-markdown"><p>I started a newsletter in January. It is called <a href="http://https://tips.rstankov.com/">Rado's Tips</a>.</p>
<p>You can check and subscribe at 👉 <a href="https://tips.rstankov.com">https://tips.rstankov.com</a> 😇</p>
<p>I'll be sharing with you tips and stories. You can expect topics around software engineering, product development, management, productivity, and ... <em>everything else that comes to my mind.</em> 🚀</p>
<p>If you</p></div>]]></description><link>https://blog.rstankov.com/i-started-a-newsletter/</link><guid isPermaLink="false">6612687375e2f800023af364</guid><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Sun, 07 Apr 2024 09:37:29 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1466096115517-bceecbfb6fde?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMjc2N3wwfDF8c2VhcmNofDR8fG5ld3NsZXR0ZXJ8ZW58MHx8fHwxNzEyNDgyNjI1fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1466096115517-bceecbfb6fde?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxMjc2N3wwfDF8c2VhcmNofDR8fG5ld3NsZXR0ZXJ8ZW58MHx8fHwxNzEyNDgyNjI1fDA&ixlib=rb-4.0.3&q=80&w=1080" alt="I started a newsletter"><p>I started a newsletter in January. It is called <a href="http://https://tips.rstankov.com/">Rado's Tips</a>.</p>
<p>You can check and subscribe at 👉 <a href="https://tips.rstankov.com">https://tips.rstankov.com</a> 😇</p>
<p>I'll be sharing with you tips and stories. You can expect topics around software engineering, product development, management, productivity, and ... <em>everything else that comes to my mind.</em> 🚀</p>
<p>If you have any questions or comments, you can ping me on <a href="https://www.threads.net/@rstankov">Threads</a>, <a href="https://www.linkedin.com/in/radoslavstankov/">LinkedIn</a>, <a href="https://mastodon.social/@rstankov">Mastodon</a> or <a href="https://twitter.com/rstankov">Twitter</a>. 📭</p>
</div>]]></content:encoded></item><item><title><![CDATA[Getting a Feature From Console to Production]]></title><description><![CDATA[A 5 steps pattern for feature development, I noticed at Angry Building. Start with Rails console and with exposing the feature with UI.]]></description><link>https://blog.rstankov.com/feature-from-console-to-everyone/</link><guid isPermaLink="false">64ce3696909b2a0002c86702</guid><category><![CDATA[product]]></category><category><![CDATA[angrybuilding]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Sun, 06 Aug 2023 07:32:07 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1668289077257-d821c3684280?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMjc2N3wwfDF8c2VhcmNofDh8fG5pY2UlMjBidWlsZGluZ3xlbnwwfHx8fDE2OTEyMzc3ODJ8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1668289077257-d821c3684280?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxMjc2N3wwfDF8c2VhcmNofDh8fG5pY2UlMjBidWlsZGluZ3xlbnwwfHx8fDE2OTEyMzc3ODJ8MA&ixlib=rb-4.0.3&q=80&w=1080" alt="Getting a Feature From Console to Production"><p>Recently, I noticed a lot of features in <a href="https://angrybuilding.com/">Angry Building</a> have the following pattern:</p>
<ol>
<li>I go to the <a href="https://guides.rubyonrails.org/command_line.html">Rails console</a> in production and do some operation</li>
<li>I copy the code, which I executed in the Rails console, into a file wrapped in a function, which I can copy/paste into the console.</li>
<li>Move the function into a service object in the main codebase, cover it with tests, and handle all edge cases.</li>
<li>Wrap service object with form object and expose it with web form only to admin users</li>
<li>Expose the form to users - this operation was battle-tested by this time, and we know all of it has implications.</li>
</ol>
<h2 id="example">Example</h2>
<p>One example of such a feature is our change of payment's apartment.</p>
<p>In <a href="https://angrybuilding.com/">Angry Building</a>, we track tax payments made by the apartment to the facility.</p>
<p>We allow importing payments from bank transfers, where we create payment records for apartments based on bank transfers.</p>
<p>For some transactions, we determine which apartment has made the payment. Then an operator can assign the apartment manually. Mistakes can happen here, and we need to re-assign the apartment.</p>
<p>Over time, as we get more customers using this feature, we get more requests for change payment apartments.</p>
<p>So change the payment apartment feature following the 1 -&gt; 5 steps to be implemented.</p>
<h2 id="discussion">Discussion</h2>
<p><em>Now, I know what you are thinking</em> - Why don't you <em>just</em> go to the 4 or 5 (<em>implement in UI</em>) steps, why spread work around? ?</p>
<p>Some features go to the 4 or 5 steps directly. It is a judgment call.</p>
<p>If I had to implement UI and expose everything I had to do in the console, my software would be bloated, and I would need a bigger team.</p>
<p>Many &quot;features&quot; fizzle out in steps 1 or 2 (<em>execute directly in the production console</em>) are caused by bugs or confusing UI.</p>
<p>When I get a request that requires me to go to the console - I try to do a root cause analysis to see how to prevent this. I have increased the overall system quality so much because of this.</p>
<p>When we reach step 3 (<em>move to code as service object covered by tests</em>), the threshold to become &quot;a real&quot; feature is passed. Then moving to step 4/5 is more about work priority and how dangerous it is to expose this feature to everyone.</p>
<p>Then is more about how often we get the request to call this service object.</p>
<p>The features implemented this way are based on support requests. Not all features go through those steps.</p>
<p>If you have any questions or comments, you can ping me on <a href="https://www.linkedin.com/in/radoslavstankov/">LinkedIn</a>, <a href="https://mastodon.social/@rstankov">Mastodon</a> or <a href="https://twitter.com/rstankov">Twitter</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Luck is a Quality]]></title><description><![CDATA[<div class="kg-card-markdown"><p>When I was younger, I was often told - &quot;Luck is quality&quot;. I hadn't put much thought into it back then. It sounded cool and helped me gather the courage to leave my comfort zone. Later in life, I realized I was right. &quot;Luck is quality&quot;</p></div>]]></description><link>https://blog.rstankov.com/luck-is-a-quality/</link><guid isPermaLink="false">64691cb472428300023a2ae8</guid><category><![CDATA[thoughts]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Sat, 20 May 2023 19:31:10 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1573156519551-3a0fc6a4236c?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMjc2N3wwfDF8c2VhcmNofDEzfHxsdWNrfGVufDB8fHx8MTY4NDYxMDI2Nnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1573156519551-3a0fc6a4236c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxMjc2N3wwfDF8c2VhcmNofDEzfHxsdWNrfGVufDB8fHx8MTY4NDYxMDI2Nnww&ixlib=rb-4.0.3&q=80&w=1080" alt="Luck is a Quality"><p>When I was younger, I was often told - &quot;Luck is quality&quot;. I hadn't put much thought into it back then. It sounded cool and helped me gather the courage to leave my comfort zone. Later in life, I realized I was right. &quot;Luck is quality&quot; Here is why I think so.</p>
<p>Let's break down the concept of luck. What is it? In my view, luck is a blend of two crucial components:</p>
<ol>
<li><strong>Opportunity</strong></li>
<li><strong>Ability</strong> to convert that opportunity into a success</li>
</ol>
<p>&quot;Luck&quot; is compounding. The more successful you are, the more opportunities arrive, and you gain more skills and abilities to take advantage of those.</p>
<p>So, how do we become &quot;lucky&quot;?</p>
<ol>
<li><strong>Step out of your comfort zone</strong>. Luck rarely comes knocking at your door while you're sitting at home. It is found in the field, amidst the hustle and bustle of life, in the effort of pushing boundaries and exploring new horizons.</li>
<li><strong>Educate yourself</strong>. Luck favors the prepared mind. <a href="https://nav.al/specific-knowledge">Cultivate specific knowledge</a> and skills pertinent to your goals. This will increase your odds of recognizing and seizing opportunities when they arise.</li>
</ol>
<p>The risk lies in assuming invincibility amidst a streak of successes. One might start to believe that their good fortune is limitless or that they've mastered all there is. They may overlook the need to enhance their skills and adapt to the evolving dynamics of their field.</p>
<p><em>(I'd recommend reading &quot;<a href="https://www.goodreads.com/book/show/84525.What_Got_You_Here_Won_t_Get_You_There">What Got You Here Won't Get You There</a>&quot; ? and &quot;<a href="https://www.goodreads.com/book/show/60097435-quit">Quit</a>&quot; ? on this subject)</em></p>
<p>So, what should you do when you're on a roll?</p>
<ol>
<li><strong>Seize more opportunities</strong>. Luck breeds luck. Use your existing momentum to explore further avenues for growth and success.</li>
<li><strong>Pay It Forward</strong>. Extend your support to others, fostering a culture of shared success and mutual growth.</li>
<li><strong>Exercise caution</strong>. Balance is key. Avoid overextending yourself. Don't  mistake luck for skills. Keep these tips in mind:
<ul>
<li>Steer clear of illegal or immoral actions.</li>
<li>Dodge actions that might lead to bankruptcy.</li>
<li>Avoid actions you'd be ashamed to share with a trusted friend.</li>
<li>Resist believing that things will work out without effort.</li>
<li>And lastly, guard against envy which leads to status games and greed.</li>
</ul>
</li>
</ol>
<p>Remember, luck is not a mystical force that arbitrarily blesses some while overlooking others. Instead, it's a product of hard work, skill, and an opportunistic mindset. So go ahead, make your luck, and create a path toward success.</p>
<p>If you have any questions or comments, you can ping me on <a href="https://www.linkedin.com/in/radoslavstankov/">LinkedIn</a>, <a href="https://mastodon.social/@rstankov">Mastodon</a> or <a href="https://twitter.com/rstankov">Twitter</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[My favourite Ruby on Rails engines]]></title><description><![CDATA[Engines is one of my favorite features in Ruby on Rails that distinguishes it from other web frameworks. Here are some cool ones...]]></description><link>https://blog.rstankov.com/top-8-ruby-on-rails-engines/</link><guid isPermaLink="false">63f9dfb5a71733000255c891</guid><category><![CDATA[ruby]]></category><category><![CDATA[GraphQL]]></category><category><![CDATA[Rails]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Sun, 26 Feb 2023 21:14:12 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1594734349446-4a2f752392d3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMjc2N3wwfDF8c2VhcmNofDN8fHRyYWluJTIwZW5naW5lfGVufDB8fHx8MTY3NzQzMzg1OA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1594734349446-4a2f752392d3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjc2N3wwfDF8c2VhcmNofDN8fHRyYWluJTIwZW5naW5lfGVufDB8fHx8MTY3NzQzMzg1OA&ixlib=rb-4.0.3&q=80&w=1080" alt="My favourite Ruby on Rails engines"><p><a href="https://guides.rubyonrails.org/engines.html">Ruby on Rails Engines</a> is one of the most powerful and underestimated features. It distinguishes Rails from other web frameworks.</p>
<h2 id="whatengines">What engines?</h2>
<p>Engines are embeddable applications that can be integrated into your Ruby on Rails application. They are distributed as regular Ruby gems with models, controllers, and views.</p>
<p>The UI that comes with engines is incredibly useful, and most engines can be set up within 10 minutes, saving months of integration of microservices and external SaaS tools.</p>
<p>Here are the top eight Ruby on Rails engines:</p>
<ol>
<li><strong>Sidekiq</strong> - background jobs</li>
<li><strong>Blazer</strong> - business intelligence</li>
<li><strong>Lookbook</strong> - UI components previews</li>
<li><strong>Flipper</strong> - Feature toggles</li>
<li><strong>Split</strong> - A/B testing</li>
<li><strong>PGHero</strong> - PostgreSQL monitoring</li>
<li><strong>FastEntry</strong> - cache previewer</li>
<li><strong>GraphiQL</strong> - GraphQL IDE</li>
</ol>
<h3 id="1sidekiq">1) <a href="https://sidekiq.org/">Sidekiq</a></h3>
<p>Sidekiq is the de facto standard for background jobs in Ruby land. The engine provides a UI to monitor what is happening with your background jobs.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_sidekiq.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_sidekiq.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="2blazer">2) <a href="https://github.com/ankane/blazer">Blazer</a></h3>
<p>Blazer allows you to write SQL queries and build charts and dashboards from them, making it an excellent tool for business intelligence.</p>
<p>But even if you don't use it as a business intelligence tool. It is a great developer tool. It can help you see what is in your database in local or staging environments.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_blazer.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_blazer.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="3lookbook">3) <a href="https://lookbook.build/">Lookbook</a></h3>
<p><em>I found this recently.</em></p>
<p>If you use <a href="https://viewcomponent.org/">ViewComponents</a> to build UI elements instead of <a href="https://guides.rubyonrails.org/action_view_helpers.html">Action View Helpers</a>, you should use it. It builds on top of <a href="https://viewcomponent.org/guide/previews.html">previews</a> and wraps them in <a href="https://storybook.js.org/">Storybook</a> like interface.</p>
<p>However, if you have ever tried to set up <a href="https://storybook.js.org/">Storybook</a> and <a href="https://reactjs.org/">React</a> components, you have spent A LOT of time on it.</p>
<p>This gem takes about 10 minutes to set up, and the rest is just writing simple <a href="https://viewcomponent.org/guide/previews.html">previews</a> Ruby classes.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_lookbook.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_lookbook.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="4flipper">4) <a href="https://github.com/jnunemaker/flipper">Flipper</a></h3>
<p>Flipper provides infrastructure and UI for <a href="https://en.wikipedia.org/wiki/Feature_toggle">Feature toggle</a>.</p>
<p>I have <a href="https://blog.rstankov.com/feature-flags-in-react/">written</a> before how to integrate with React - <a href="https://blog.rstankov.com/feature-flags-in-react/">here</a>.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_flipper.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_flipper.png" alt="My favourite Ruby on Rails engines"></a>
<p>Flipper has a <a href="https://www.flippercloud.io/">paid version</a> with features like permissions and audit history.</p>
<h3 id="5split">5) <a href="https://github.com/splitrb/split">Split</a></h3>
<p><a href="https://github.com/splitrb/split">Split</a> provides infrastructure for <a href="https://en.wikipedia.org/wiki/A/B_testing">A/B testing</a>.</p>
<p>There are a lot of SaaS tools to do so; however, they are slowing your pages, cost a lot and take time integrate.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_split_2.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_split_2.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="6pghero">6) <a href="https://github.com/ankane/pghero">PGHero</a></h3>
<p>It provides a performance dashboard <a href="https://www.postgresql.org/">PostgreSQL</a>. It tells you things like - slow queries, unused indexes, table/index sizes, and more.</p>
<p>It comes from ? <a href="https://www.instacart.com">Instacard</a>, which makes also <a href="https://github.com/ankane/blazer">Blazer</a> and <a href="https://www.instacart.com/opensource">other wonderful open source projects</a>.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_pghero.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_pghero.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="7fastentry">7) <a href="https://rubygems.org/gems/fastentry">FastEntry</a></h3>
<p>Do you know what is in your cache? <a href="https://rubygems.org/gems/fastentry">FastEntry</a> shows you this. <em>Simple and useful.</em></p>
<p>I have found a secondary use for it in development. I use the same Redis instance for cache, <a href="https://sidekiq.org/">Sidekiq</a>, and application data storage. It shows you everything in this Redis instance.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_fast_entry.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_fast_entry.png" alt="My favourite Ruby on Rails engines"></a>
<h3 id="8graphiql">8) <a href="https://github.com/rmosolgo/graphiql-rails">GraphiQL</a></h3>
<p><em>Last but not least.</em> <a href="https://github.com/graphql/graphiql">GraphiQL</a> is IDE for <a href="https://graphql.org/">GraphQL</a>. This engine provides quick setup and integration.</p>
<a href="https://rstankov-blog.s3.amazonaws.com/rails_engine_graphiql.png" style="box-shadow: none;" target="_blank">
<img src="https://rstankov-blog.s3.amazonaws.com/rails_engine_graphiql.png" alt="My favourite Ruby on Rails engines"></a>
<h2 id="howtosetupandsecureengines">How to setup and secure engines?</h2>
<p>I mentioned that engines are easy to set up. But talk is cheap. Let me show you the code. ?</p>
<p>Let's install <a href="https://github.com/ankane/blazer">Blazer</a>, as it is one of the more complex ones to set up.</p>
<p><strong>Step 1</strong>: Add to Gemfile</p>
<pre><code>gem &quot;blazer&quot;
</code></pre>
<p><strong>Step 2</strong>: Bundle install &amp; database setup</p>
<pre><code>bundle install

rails generate blazer:install
rails db:migrate
</code></pre>
<p><strong>Step 3</strong>: Mount to the Rails application</p>
<pre><code class="language-ruby"># config/routes.rb
namespace :dev, constraints: Routes::AdminConstraint do
  mount Blazer::Engine, at: '/blazer'
  # ... other engines
end
</code></pre>
<p><em>Note:</em> <code>Routes::AdminConstraint</code> ensures that this namespace is only available for certain users.</p>
<p>A simplified version can look something like:</p>
<pre><code class="language-ruby">module Routes::AdminConstraint
  extend self

  def matches?(request)
    return false if request.session[:user_id].blank?

    User.find(request.session[:user_id]).admin?
  end
end
</code></pre>
<p><strong>In Production</strong>: Have database as ENV variable</p>
<pre><code>ENV[&quot;BLAZER_DATABASE_URL&quot;] = &quot;postgres://user:password@hostname:5432/database&quot;
</code></pre>
<p>That's it, you have a business intelligence tool ?</p>
<h2 id="conclusion">Conclusion</h2>
<p>Engines have saved me a lot of time <em>(and use of external services)</em> over the years. I'm constantly on the lookup for new useful ones  ?</p>
<p>If you have any questions or comments, you can ping me on  <a href="https://twitter.com/rstankov">Twitter</a> or <a href="https://mastodon.social/@rstankov">Mastodon</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[How to level up you engineering skills?]]></title><description><![CDATA[<div class="kg-card-markdown"><p>A good way for developers to level up is, instead of reading the same beginner tutorials on sites like Medium and blogs, to read documentation and source code of libraries they use.</p>
<p>I often open the source of code of <a href="https://github.com/rails/rails">Ruby on Rails</a>, <a href="https://github.com/rspec">RSpec</a> and <a href="https://github.com/rspec">React</a>.</p>
<p>Recently, I have been</p></div>]]></description><link>https://blog.rstankov.com/how-to-level-up-you-engineering-skills/</link><guid isPermaLink="false">63d8092317a40a0004b982db</guid><category><![CDATA[thoughts]]></category><category><![CDATA[open_source]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Mon, 30 Jan 2023 18:33:46 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1661961112835-ca6f5811d2af?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMjc2N3wxfDF8YWxsfDF8fHx8fHwyfHwxNjc1MTAzNDE2&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1661961112835-ca6f5811d2af?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjc2N3wxfDF8YWxsfDF8fHx8fHwyfHwxNjc1MTAzNDE2&ixlib=rb-4.0.3&q=80&w=1080" alt="How to level up you engineering skills?"><p>A good way for developers to level up is, instead of reading the same beginner tutorials on sites like Medium and blogs, to read documentation and source code of libraries they use.</p>
<p>I often open the source of code of <a href="https://github.com/rails/rails">Ruby on Rails</a>, <a href="https://github.com/rspec">RSpec</a> and <a href="https://github.com/rspec">React</a>.</p>
<p>Recently, I have been reading <a href="https://github.com/mastodon/mastodon">Mastodon</a> and <a href="https://github.com/wbkd/react-flow">React-Flow</a>.</p>
<h3 id="whatwillonegainingbydoingso">What will one gaining by doing so?</h3>
<ol>
<li><strong>Learn to read code.</strong> Most of the time, developers are not writing code.- they are reading.</li>
<li><strong>Learn techniques in the real world.</strong> Books and blog posts often give us simplified version it is more understandable. Real life is messy.</li>
<li><strong>Find out about useful helpers in those libraries.</strong> For example, even today, I found an about a new Rails-RSpec matcher built years ago. This will save me a lot of time in the future.</li>
<li><strong>Understand everything is built by other humans.</strong> This one is more meta. So often, I see junior developers take for granted that library works this way.</li>
<li><strong>Debugging.</strong> I can't recall how often I had to dive deep into the library source code to trace a weird behavior in our system.</li>
</ol>
<h3 id="personalstory">Personal story</h3>
<p>This was around 2005 or 2006. Back then, I was trying to learn JavaScript. Every piece of JavaScript code seemed to me like magic.<br>
The first time I had <em>&quot;I got it and now understand what this is doing&quot;</em> was when I was reading the source code of <a href="http://prototypejs.org/">Prototype.JS</a>. This was while reading some &quot;prototype&quot; and &quot;DOM&quot; code. I can still recall the joy I felt at this moment. ?</p>
</div>]]></content:encoded></item><item><title><![CDATA[Bug Duty Process]]></title><description><![CDATA[The process for handling bugs at Product Hunt.]]></description><link>https://blog.rstankov.com/bug-duty-process/</link><guid isPermaLink="false">61f2ef77ad128200042d2f94</guid><category><![CDATA[process]]></category><category><![CDATA[product hunt]]></category><category><![CDATA[Management]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Thu, 27 Jan 2022 19:51:48 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1528640936814-4460bc015292?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMjc2N3wwfDF8c2VhcmNofDF8fGJ1Z3xlbnwwfHx8fDE2NDMzMTA5OTg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1528640936814-4460bc015292?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjc2N3wwfDF8c2VhcmNofDF8fGJ1Z3xlbnwwfHx8fDE2NDMzMTA5OTg&ixlib=rb-1.2.1&q=80&w=1080" alt="Bug Duty Process"><p>One common problem in project management is managing bugs.  Over the last couple of years at <a href="https://blog.rstankov.com/bug-duty-process/producthunt.com">Product Hunt</a>, we have used the Bug Duty process to handle and deal with upcoming bugs.</p>
<p>This process handles the following problems:</p>
<ul>
<li>Where do you put bug fixes in a sprint</li>
<li>People reaching to their friend engineer to do their pet fixes or features</li>
</ul>
<p>It also has the following benefits:</p>
<ul>
<li>Helps engineers to know the codebase better
<ul>
<li>It is great for onboarding new team members</li>
</ul>
</li>
<li>Improve skills by helping engineers to learn how to
<ul>
<li>Manage a project board</li>
<li>Debug tricky code</li>
<li>Get better at avoiding known bugs</li>
</ul>
</li>
<li>Provides a pipeline where people across the company can provide feature ideas</li>
</ul>
<h2 id="bugdutyprocess">Bug Duty process</h2>
<p>A different person from engineering is assigned to the Bug Duty sprint every sprint. During this sprint, they don't work on a project. Instead, they work on &quot;Bugs&quot; project in <a href="https://asana.com/">Asana</a>.</p>
<p>The engineer has the following responsibilities:</p>
<ul>
<li>Manage the  &quot;Bugs&quot; project  in <a href="https://asana.com/">Asana</a>
<ul>
<li>Fix outstanding bugs</li>
<li><strong>Immediately fix</strong> the bugs marked as &quot;<strong>high priority</strong>&quot;</li>
</ul>
</li>
<li>Attend the Support team weekly sync, where issues with our website are often discussed</li>
<li>Be available in the <code>#product-feedback</code> Slack channel
<ul>
<li>Our teammates post issues there. The person on bug duty should handle those</li>
</ul>
</li>
<li>Handle all upcoming exceptions reported in <code>#engineering-sentry</code>
<ul>
<li>This Slack channel receives all Sentry errors. We try to keep their number to 0 in production.</li>
</ul>
</li>
</ul>
<p>The goal is for them not to do the following ?</p>
<img src="https://rstankov-blog.s3.us-east-1.amazonaws.com/bugduty_meme.jpg" alt="Bug Duty Process" style="width: 80%">
<p>The process is simple Collect -&gt; Triage -&gt; Group and Handle:</p>
<img src="https://rstankov-blog.s3.us-east-1.amazonaws.com/bugduty_flow.png" style="width:90%" alt="Bug Duty Process">
<h3 id="reportabugform">Report a bug form</h3>
<p>Bugs are reported via Asana Form and get into the &quot;Bugs&quot; project. The goal is to get enough information for debugging.</p>
<img src="https://rstankov-blog.s3.us-east-1.amazonaws.com/bugduty_report_from.png" alt="Bug Duty Process" style="width: 80%">
<h3 id="asanaproject">Asana Project</h3>
<p>We use <a href="https://asana.com/">Asana</a> for project management. The &quot;Bugs&quot; project looks like the following:</p>
<img src="https://rstankov-blog.s3.us-east-1.amazonaws.com/bugduty_project.png" alt="Bug Duty Process" style="width: 80%">
<p>Here is what each section means:</p>
<ul>
<li>&quot;Just reported&quot; - Where reported bugs arrive. They should be handled during the day.</li>
<li>&quot;QA reported&quot; - We use the external agency <a href="https://www.lodestoneco.com/">Lodestone</a> for QA. They do a lot of exploratory testing and report bugs in this column</li>
<li>&quot;Bugs&quot; - Those are known and triaged bugs from &quot;Just reported&quot;</li>
<li>&quot;Next up&quot; - Bugs to be fixed next</li>
<li>&quot;In progress &quot; - In-progress fixes</li>
<li>&quot;In review&quot; - Fix is ready, now it's in code review</li>
<li>&quot;Awaiting confirmation&quot; - Waiting for a bug fix to be confirmed by QA or the person who reported</li>
<li>&quot;Fixed&quot; - List of fixed bugs</li>
<li>&quot;Won't fix&quot; - These are either not bugs, or bugs that won't be fixed</li>
<li>&quot;Feature suggestions&quot; - Feature ideas collected from the team. PMs look through those to add to various projects backlogs</li>
</ul>
<h3 id="firstday">First day</h3>
<p>When someone starts their bug duty</p>
<ul>
<li>They should assign themselves as project owner of the &quot;Bugs&quot; project</li>
<li>They should assign themselves to the <code>#product-feedback</code> Slack channel</li>
<li>Talk with the person previously on bug duty</li>
<li>Check the status of in-progress bugs</li>
<li>Check all existing bugs</li>
</ul>
<h3 id="lastday">Last day</h3>
<p>Follow the <a href="https://wiki.c2.com/?BoyScoutRule">Scout Rule</a> - leave the project in better shape than you found it.</p>
<ol>
<li>Make sure to close all ongoing things they are working on.</li>
<li>Unassign from tasks and move out of &quot;Next up&quot;.</li>
<li>Talk with the next person on Bug Duty about any urgent and important bugs you think will make sense for them to work on next.</li>
<li>Do a retrospective about how you did this bug duty.
<ul>
<li>What were common issues you faced?</li>
<li>How can we improve our code or Bug Duty process?</li>
</ul>
</li>
</ol>
<h3 id="schedule">Schedule</h3>
<p>We keep the schedule of who is on bug duty on Notion. It is negotiated between team leads which should be on bug duty</p>
<img src="https://rstankov-blog.s3.us-east-1.amazonaws.com/bugduty_project.png" alt="Bug Duty Process" style="width: 80%">
<h2 id="failuremodes">Failure modes</h2>
<p>There are a couple of failure modes we have faced.</p>
<p><strong>&quot;<em>There are too many for a single person to handle</em>&quot;</strong></p>
<p>We were in this situation once, after multiple migrations and redesigns. We made a <a href="https://en.wikipedia.org/wiki/Bug_bash">&quot;Bug Bash&quot;</a> sprint when everyone was on fixing bugs and gave out <a href="https://ph-static.imgix.net/golden-kitty/2020/EventTrophy.png">Golden Kitty awards</a> to the people who reported and fixed most bugs.</p>
<p><strong>&quot;<em>I don't understand this area of the system enough to fix the issue</em>&quot;</strong></p>
<p>This happens often. The solution is to find someone who knows this area and do <a href="https://en.wikipedia.org/wiki/Pair_programming">pairing programming sessions</a> to fix the issue.</p>
<p><strong>&quot;<em>Engineer on bug duty slacks and don't take on important bugs</em>&quot;</strong></p>
<p>It is up to their team lead to monitor their performance. Having more formalized handoff/retro can also help with this.</p>
<p><strong>&quot;<em>We don't have enough engineers. So none can be spared to be on bug duty.</em>&quot;</strong></p>
<p>This is a resources and planning problem. We solve this by using Bug Duty for onboarding or some team leads standing in on Bug Duty.</p>
<h2 id="conclusion">Conclusion</h2>
<p>We have tweaked and adjusted this process over the last couple of years. It helped us keep our system clean of bugs ?</p>
<p>If you have any questions or comments, you can ping me on  <a href="https://twitter.com/rstankov">Twitter</a> .</p>
</div>]]></content:encoded></item><item><title><![CDATA[Product Hunt Architecture]]></title><description><![CDATA[How we combine React, Next.js, GraphQL, Apollo, and Ruby on Rails ]]></description><link>https://blog.rstankov.com/product-hunt-architecture/</link><guid isPermaLink="false">60ab88f99c99d10004cd34c0</guid><category><![CDATA[React]]></category><category><![CDATA[ruby]]></category><category><![CDATA[product hunt]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Mon, 24 May 2021 11:43:50 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1451976426598-a7593bd6d0b2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMjc2N3wwfDF8c2VhcmNofDZ8fGFyY2hpdGVjdHVyZXxlbnwwfHx8fDE2MjE4NTY0NDI&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1451976426598-a7593bd6d0b2?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjc2N3wwfDF8c2VhcmNofDZ8fGFyY2hpdGVjdHVyZXxlbnwwfHx8fDE2MjE4NTY0NDI&ixlib=rb-1.2.1&q=80&w=1080" alt="Product Hunt Architecture"><p>Last year we <a href="https://www.producthunt.com/stories/product-hunt-s-new-ceo-josh-buckley">very</a> <a href="https://www.producthunt.com/stories/product-hunt-s-new-gm-ashley-higgins">eventful</a> for <a href="https://www.producthunt.com/">Product Hunt</a>. On technical level we closed large system migrations like fully switch to <a href="https://www.producthunt.com/stories/product-hunt-s-new-gm-ashley-higgins">Next.js</a> for frontend, moved to <a href="https://blog.rstankov.com/product-hunt-architecture/typescriptlang.org">TypeScript</a> and rewrote our mobile app with <a href="https://reactnative.dev/">React.Native</a>.</p>
<p>Here is how our technical architecture looks currently:</p>
<img src="https://rstankov-blog.s3.amazonaws.com/producthunt_architecture_2021.png" style="width: 100%" alt="Product Hunt Architecture">
<p>I have given detail <a href="https://rstankov.com/appearances">talks</a> about each section of our systems.</p>
<ul>
<li>Backend - Rails, GraphQL, AWS, Docker, PostgreSQL (<a href="https://speakerdeck.com/rstankov/3-years-graphql-at-product-hunt">slides</a> ,  <a href="https://www.youtube.com/watch?v=0Bwg03f_3KQ">video</a>)</li>
<li>Frontend - React, TypeScript, Next.js, Apollo (<a href="https://speakerdeck.com/rstankov/react-architecture-at-product-hunt">slides</a> ,  <a href="https://www.youtube.com/watch?v=n1SwPPfqLlI">video</a>)</li>
<li>Mobile - React Native, TypeScript, Apollo (<a href="https://speakerdeck.com/rstankov/react-native-architecture-at-product-hunt">slides</a>, <a href="https://youtu.be/REg_EVXStMo?t=5521">video</a>)</li>
</ul>
<p>Here is what a web request goes through to deliver a <a href="https://www.producthunt.com/posts/focused-task">page</a> from the site:</p>
<img src="https://rstankov-blog.s3.amazonaws.com/producthunt_request_flow_chart.png" alt="Product Hunt Architecture" style="width: 100%">
<p>Overall, I'm very happy with our current system. It allows us to handle our traffic and ship fast and reliable.</p>
<h3 id="whatscomesnext">Whats comes next?</h3>
<p><strong>Software is never done</strong> and this is true for us. We already had started improving on our foundations like:</p>
<ul>
<li>better core React components</li>
<li>kinda, moving towards <a href="https://speakerdeck.com/rstankov/domain-driven-design-kinda">Domain Driven Design</a>.</li>
<li>improving on backend infrastructure for better performance and monitoring</li>
</ul>
<p>For any questions or comments, you can ping me on <a href="https://twitter.com/rstankov">Twitter</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Design Code Like It Will Be Removed Tomorrow]]></title><description><![CDATA[<div class="kg-card-markdown"><p>In two of my most popular blog posts - &quot;<a href="https://blog.rstankov.com/collaborative-single-player-mode">Collaborative Single Player Mode</a>&quot; and &quot;<a href="https://blog.rstankov.com/how-i-plan-and-execute-features/">Feature Planning</a>&quot;, I mentioned one of my coding principles:</p>
<pre><code>Write code as if you are going to remove it tomorrow.
</code></pre>
<p>I get a lot of questions about this. What do I mean</p></div>]]></description><link>https://blog.rstankov.com/design-code-for-removal/</link><guid isPermaLink="false">609d84aa8c172f0004dc1be5</guid><category><![CDATA[thoughts]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Mon, 17 May 2021 10:59:03 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1579403124614-197f69d8187b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMjc2N3wwfDF8c2VhcmNofDY0fHxjb2RlfGVufDB8fHx8MTYyMDkzNTk5Mg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1579403124614-197f69d8187b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjc2N3wwfDF8c2VhcmNofDY0fHxjb2RlfGVufDB8fHx8MTYyMDkzNTk5Mg&ixlib=rb-1.2.1&q=80&w=1080" alt="Design Code Like It Will Be Removed Tomorrow"><p>In two of my most popular blog posts - &quot;<a href="https://blog.rstankov.com/collaborative-single-player-mode">Collaborative Single Player Mode</a>&quot; and &quot;<a href="https://blog.rstankov.com/how-i-plan-and-execute-features/">Feature Planning</a>&quot;, I mentioned one of my coding principles:</p>
<pre><code>Write code as if you are going to remove it tomorrow.
</code></pre>
<p>I get a lot of questions about this. What do I mean by that?</p>
<p>When you write code for a new feature, you want it to be decoupled from other parts of your system. Put it in its own bucket - namespace, module, directory, etc. (depending on the technology). This makes the code easier to test and reduces the risk it will break existing functionality.</p>
<p>I have found that a good way to tell how decoupled some code is to find out how much work it would be to remove the feature it is powering. If removing a feature is just remove a folder + 2~3 integration points - that's good code.</p>
<p>Because of this, I prefer to organize code based on domain than on type.</p>
<h2 id="buthowoftenyouremovefeatures">But how often you remove features? ?</h2>
<p>At <a href="https://www.producthunt.com/">Product Hunt</a>, we do this quite often ?  , and this is a good thing. When you do product development, you discover what works and what doesn't via experimentation.</p>
<h2 id="butispentsomuchworkonthisfeatureandnowmyworkgoestowaste">But I spent so much work on this feature, and now my work goes to waste? ?</h2>
<p>Not really. On the product level, you learned something, and hopefully, this will give you a better direction for future work.</p>
<p>Product logic gets simpler. Think about losing body fat.</p>
<h2 id="butihadsomereallyreallycoolcodethere">But I had some really, really cool code there? ?</h2>
<p>When I write code, I make a clear distinction between <a href="https://blog.rstankov.com/plumbing-vs-domain/">Plumbing vs. Domain</a>. I have found that plumbing utilities are often where the &quot;elegant&quot; and &quot;cool&quot; code is.</p>
<p>This often happens to me. I build a feature and separate its plumbing utilities and domain. Then, for another feature, either me or a teammate use the utilities. Later, the original feature is removed, and the utilities stay.</p>
<p>If utilities are isolated, they are often re-used in different domains. So this cool code stays after the feature.</p>
<p>For any questions or comments, you can ping me on <a href="https://twitter.com/rstankov">Twitter</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Year One of Focused Task]]></title><description><![CDATA[A year ago during the first COVID-19 lockdown, I started working on a small macOS menubar app. I used Electron, React and Redux and it is open source.]]></description><link>https://blog.rstankov.com/focus-task-first-year/</link><guid isPermaLink="false">6081ce72acae370004114012</guid><category><![CDATA[open_source]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Mon, 26 Apr 2021 13:03:19 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1589959864690-24091a905ea1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMjc2N3wwfDF8c2VhcmNofDR8fHRhcmdldHxlbnwwfHx8fDE2MTkyMTI0OTQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1589959864690-24091a905ea1?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjc2N3wwfDF8c2VhcmNofDR8fHRhcmdldHxlbnwwfHx8fDE2MTkyMTI0OTQ&ixlib=rb-1.2.1&q=80&w=1080" alt="Year One of Focused Task"><p>A <a href="https://github.com/RStankov/FocusedTask/commit/b340ffca6c036da9a7a9a8b4e388e1b90847f72d">year ago</a>  during the first COVID-19 lockdown, I started working on a small macOS menubar app called ? <a href="https://github.com/RStankov/FocusedTask">FocusedTask</a>. I used <a href="https://www.electronjs.org/">Electron</a>, <a href="https://reactjs.org/">React</a> and <a href="https://redux.js.org/">Redux</a> and it is <a href="https://github.com/RStankov/FocusedTask">open source</a>.</p>
<img src="https://raw.githubusercontent.com/RStankov/FocusedTask/master/assets/screenshot.png" alt="Year One of Focused Task" width="600">
<div style="text-align: center">
  ? <a href="https://www.youtube.com/watch?v=Vp2ASWq-S04&ab_channel=RadoslavStankov">See it in action</a> ?
</div>
<p>When I work on a task, I usually need 3 things - a quick list of nested todos, a list of links related to my task, and an area to scribble notes</p>
<p>Last year, I was switching between a <a href="https://blog.bear.app/2017/09/bear-tips-pin-notes-to-the-top-to-stay-on-task/">pinned note</a> in <a href="https://bear.app/">Bear</a> and projects in <a href="https://todoist.com/app/">Todoist</a>.</p>
<p>I'm a big fan of macOS menu bar apps and shortcuts, so I decided to combine those two and build <a href="https://github.com/RStankov/FocusedTask">FocusedTask</a>.</p>
<p>After just 2 days of coding, I was able to start using it.</p>
<p>The hardest part of the app was to get a paid Apple developer account <em>(they didn't like my Bulgarian debit card)</em> and <a href="https://kilianvalkhof.com/2019/electron/notarizing-your-electron-application/">making the notarizing to work</a>. ?‍♂️</p>
<p>I still use Bear and Todoist for other parts of my workflow.</p>
<p>I launched the app on <a href="https://www.producthunt.com/posts/focused-task">Product Hunt</a> with moderate success.</p>
<p><a href="https://www.producthunt.com/posts/focused-task?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-focused-task" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=264189&theme=light" alt="Year One of Focused Task" style="width: 250px; height: 54px;" width="250" height="54"></a></p>
<p>According to my logs, currently, 29 people use the app daily, which makes me very happy ?</p>
<p>I got a couple of contributions to it:</p>
<ul>
<li>UI/UX improvements from <a href="https://twitter.com/vestimir">Vestimir</a></li>
<li>Dark theme, settings, and more shortcuts from <a href="https://github.com/tjhdev">TJHdev</a></li>
</ul>
<p>I have given a couple of talks about the code structure of Focused Task</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/nGgaak8MEmg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<h2 id="conclusion">Conclusion</h2>
<p>I don't have any grandiose plans for <a href="https://github.com/RStankov/FocusedTask">FocusedTask</a>. It is just a small tool I built for myself. I learned a lot while making it.</p>
<p>I'm very happy that other people use it too.</p>
<p>It is a win in my book. ?</p>
<p>You can download it from ? <a href="http://focused-task.herokuapp.com/download?ref=github">here</a> or check its source.</p>
<p>For any questions or comments, you can ping me on <a href="https://twitter.com/rstankov">Twitter</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[4 Techniques for Structuring Next.js Applications]]></title><description><![CDATA[The way I structure React applications for the last couple of years. The focus is on decoupling and making it obvious what is used where.]]></description><link>https://blog.rstankov.com/structuring-next-js-application/</link><guid isPermaLink="false">607bd5b77a253a00047f1a82</guid><category><![CDATA[React]]></category><category><![CDATA[GraphQL]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Tue, 20 Apr 2021 07:02:13 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1561883088-039e53143d73?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMjc2N3wwfDF8c2VhcmNofDEyfHxjb2RlJTIwcmVhY3R8ZW58MHx8fHwxNjE4Nzc3Mzcx&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1561883088-039e53143d73?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjc2N3wwfDF8c2VhcmNofDEyfHxjb2RlJTIwcmVhY3R8ZW58MHx8fHwxNjE4Nzc3Mzcx&ixlib=rb-1.2.1&q=80&w=1080" alt="4 Techniques for Structuring Next.js Applications"><p><a href="https://nextjs.org/">Next.js</a> is an excellent tool. I see it as <a href="https://rubyonrails.org/">Ruby on Rails</a> for <a href="https://reactjs.org/">React</a>.</p>
<p>I have been using <a href="https://nextjs.org/">Next</a> as starting point for React applications for about 3 years. Last December, me and my team were able to migrate Product Hunt to Next.</p>
<p>Here are some of the techniques I use to structure <a href="https://nextjs.org/">Next</a> applications:</p>
<ol>
<li>Keep screens out of <code>page</code> folder</li>
<li>Wrap <a href="https://nextjs.org/">Next</a>'s <code>Link</code> component</li>
<li>Have a <code>paths</code> helper to generate links</li>
<li>Build each page via a <code>createPage</code> helper</li>
</ol>
<h2 id="technique1keepscreensoutofthepagesfolder">Technique 1: Keep screens out of the <code>pages</code> folder</h2>
<p><a href="https://nextjs.org/">Next</a> uses a file-based router. It is lightweight and gives you a good overview of your pages. However, pages tend to have many &quot;private&quot; components, which you want to keep near the page code. Also, the URL structure of your application is often different than the domain structure.</p>
<p>Because of this, I use the following directory structure:</p>
<pre><code>~/components/
~/screens/
~/pages/
</code></pre>
<p><code>pages</code> is the <a href="https://nextjs.org/">Next</a> router. Files there are just imports of components from <code>screens</code>.</p>
<pre><code class="language-js">// ~/pages/posts/[id].ts
import page from '~/screens/posts/show'

export default page;
</code></pre>
<p><code>screens</code> is where the actual pages live. They are grouped by domain. Everything in this folder is considered &quot;private&quot; and can't be imported from the outside.</p>
<pre><code>~/screens/posts/show/index.tsx - entry point of screen
~/screens/posts/show/Query.graphql - query to fetch the data
~/screens/posts/show/utils.ts - helper functions
~/screens/posts/show/PrivateComponent1.ts - some &quot;private&quot; component
~/screens/posts/show/PrivateComponent2.ts - other &quot;private&quot; component
</code></pre>
<p>All reusable components are in <code>components</code>. My definition of &quot;reusable&quot; is <em>&quot;used at least in two places&quot;</em>.</p>
<p><a href="https://blog.rstankov.com/structuring-react-components/">Here</a> you can read more about structuring components in general.</p>
<h2 id="technique2wrapnextslinkcomponent">Technique 2: Wrap Next's <code>Link</code> component</h2>
<p>Every routing library has a <code>Link</code> component. It is used instead of the <code>&lt;a&gt;</code> tag. When clicked, it changes the URL without a full page redirect, and then the routing handles loading and displaying the new page.</p>
<p>I always wrap the routing library <code>Link</code> with my own <code>Link</code> component.</p>
<p><a href="https://nextjs.org/">Next</a>'s <a href="https://nextjs.org/docs/api-reference/next/link">Link</a> component interface is a bit different than that of the others, but it functions in the same way:</p>
<pre><code class="language-js">&lt;Link href=&quot;/company&quot;&gt;
  &lt;a&gt;Company&lt;/a&gt;
&lt;/Link&gt;
</code></pre>
<p>I don't like that there are two components here - it is too cumbersome for my taste. This alone is a reason enough to wrap it.</p>
<p>Wrapping it also makes it easier to migrate to something like <a href="https://remix.run/">Remix</a> or <a href="https://www.gatsbyjs.com/">Gatsby</a>, if needed.</p>
<p>Here is my wrapped <code>Link</code>:</p>
<pre><code class="language-js">import * as React from 'react';
import Link from 'next/link';

// support both static and dynamic paths
type IPath = string | { as: string; href: string };

// allow this component to accept all properties of an &quot;a&quot; tag
interface IProps extends React.AnchorHTMLAttributes&lt;HTMLAnchorElement&gt; {
  to: IPath;
  prefetch?: boolean
  // ... more props we want to pass to Next Link
}

// Overwrite, Next default prefetch
export default React.forwardRef(({ to, prefetch = false, ...props }: IProps, ref: any) =&gt; {
  if (typeof to === 'string') {
    return (
      &lt;Link href={to} prefetch={prefetch}&gt;
        &lt;a {...props} ref={ref} /&gt;
      &lt;/Link&gt;
    );
  }
	 
  return (
    &lt;Link href={to.href} as={to.as} prefetch={prefetch}&gt;
      &lt;a {...props} ref={ref} /&gt;
    &lt;/Link&gt;
  );
});
</code></pre>
<p>Notice how I have overwritten the default <code>prefetch</code> value. Having our Link component allows us to do things like this.</p>
<p>Having such a wrapper was very useful the two times <a href="https://nextjs.org/">Next</a> changed how they handle dynamic routes.</p>
<h2 id="technique3haveapathshelpertogeneratelinks">Technique 3: Have a <code>paths</code> helper to generate links</h2>
<p>People often use hardcoded links in React applications:</p>
<pre><code class="language-js">&lt;Link to=&quot;/about&quot;&gt;About&lt;/Link&gt;
&lt;Link to=&quot;/contact&quot;&gt;Contact&lt;/Link&gt;
&lt;Link to={{ href: '/post/[id], as: '/post/1' }}&gt;Post 1&lt;/Link&gt;
</code></pre>
<p>This is brittle, not type-safe, and makes renaming URLs or changing URL structure hard.</p>
<p>I have solved this by having a <code>paths.ts</code> file that defines all routes in my apps:</p>
<pre><code class="language-js">export default {
  about: '/about',
  contact: '/contact',
  post: ({ id }: { id: string }) =&gt; { href: '/post/[id], as: `/post/${id}` }
}
</code></pre>
<p>This way, I can change the routes, and I’m protected from typos.</p>
<pre><code class="language-js">import paths from '~/paths';

&lt;Link to={paths.about}&gt;About&lt;/Link&gt;
&lt;Link to={paths.contact}&gt;Contact&lt;/Link&gt;
&lt;Link to={paths.post(1)}&gt;Post 1&lt;/Link&gt;
</code></pre>
<h2 id="technique4createpage">Technique 4: <code>createPage</code></h2>
<p>Every page entry uses the <code>createPage</code> helper. It handles the basics each page requires - data fetching, error handling, SEO tags, permissions, layout, etc.</p>
<pre><code class="language-js">// components/Component/index.js
import QUERY from './Query';
import { SomePage as IData } from 'graph/SomePage';

// 1) Create page as a default export
// makes it easy to quickly see the page options
export default createPage&lt;IData&gt;({
  // 2) Lead with Query
  query: QUERY,
  queryVariable: ({ id }) =&gt; ({ id });

  // 3) What are the page requirements
  requireLogin: 'edit post',
  requireAdmin: true,
  requireFeature: 'new-edit-page',
  requirePermissions: ({ post }) =&gt; post.canEdit,

  // 4) Rest of the options
  layout: PostLayout,

  bodyClassName: 'white-background',

  setTags: ({ post }) =&gt; post.meta,
  setStructuredData: ({ post }) =&gt; post.structuredData,

  // 5) the Page's main component
  renderComponent({ data }) {
    // render the page
  }
});
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>I had used those techniques before <a href="https://nextjs.org/">Next</a> existed. They help me to keep the whole system decoupled and make it obvious what is used where.</p>
<p>Because of this, my team and I migrated Product Hunt (a 7-year-old codebase) to Next in about a month.</p>
<p>I hope you find them useful as well. For any questions or comments, you can ping me on <a href="https://twitter.com/rstankov">Twitter</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Plumbing vs Domain]]></title><description><![CDATA[There are two types of work in feature development.
The domain is the business logic, and plumbing is the utilities we use to build the system.]]></description><link>https://blog.rstankov.com/plumbing-vs-domain/</link><guid isPermaLink="false">6060962742262900045d2c48</guid><category><![CDATA[thoughts]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Mon, 29 Mar 2021 05:54:25 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1583869367058-97af59eef352?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMjc2N3wwfDF8c2VhcmNofDIzfHxQbHVtYmluZ3xlbnwwfHx8fDE2MTY5NDI4MTA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1583869367058-97af59eef352?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjc2N3wwfDF8c2VhcmNofDIzfHxQbHVtYmluZ3xlbnwwfHx8fDE2MTY5NDI4MTA&ixlib=rb-1.2.1&q=80&w=1080" alt="Plumbing vs Domain"><p>In my blog post about <a href="https://blog.rstankov.com/how-i-plan-and-execute-features/">Feature Planning</a>, I omitted an important thing I do while developing. I split the feature work into plumbing and domain.</p>
<pre><code>Domain: User, Post, FounderClub, Comments
Plumbing: Handle::RaceCondition, Formatter, KittyEvents, SearchObject
</code></pre>
<p>I define plumbing as the infrastructure code needed to enable the business logic/rules.</p>
<p>Domain logic is connected to the business and changes as often as the business does. It is more connected to the real world. Changes come from &quot;outside&quot;, not from developers.</p>
<p>On the other hand, plumbing is in the developer's &quot;land&quot;. It is all the utilities we have in the system that defines the underlining rules of the systems. I tend to find it more stable and, thus, more comfortable to generalize.</p>
<p>An excellent example of this is <a href="https://rubyonrails.org/">Ruby on Rails</a>, it is the extracted plumbing of <a href="https://basecamp.com/">Basecamp</a>. Recently, they also extracted the <a href="https://hotwire.dev/">Hotwite</a> library, which is the plumbing around their new app <a href="https://hey.com/">Hey</a>.</p>
<p>In <a href="https://www.producthunt.com">Product Hunt</a>, we often remove features. What we keep is all the plumbing we build for them, which makes our system better.</p>
<h2 id="conclusion">Conclusion</h2>
<p>When we work on a feature, there are two types of things we usually do - plumbing and domain.</p>
<p>A domain is defined by business rules and changes often. Plumbing is the utilities we use to build our system. Those can be more generic and used in various places in our system. I call those &quot;code improvements&quot;. The rules for making a good domain and plumbing code are different.</p>
</div>]]></content:encoded></item><item><title><![CDATA[React and Graphql Optimization Story]]></title><description><![CDATA[How sometimes is better to split React component into two than adding more props. ]]></description><link>https://blog.rstankov.com/graphql-and-react/</link><guid isPermaLink="false">6053ba58a63b9b000431e76f</guid><category><![CDATA[React]]></category><category><![CDATA[refactoring]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Mon, 22 Mar 2021 15:42:59 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1523800503107-5bc3ba2a6f81?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMjc2N3wwfDF8c2VhcmNofDl8fGNvZGV8ZW58MHx8fHwxNjE2MTAwNTcz&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1523800503107-5bc3ba2a6f81?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjc2N3wwfDF8c2VhcmNofDl8fGNvZGV8ZW58MHx8fHwxNjE2MTAwNTcz&ixlib=rb-1.2.1&q=80&w=1080" alt="React and Graphql Optimization Story"><p>While doing some work on the <a href="https://www.producthunt.com/stories">Product Hunt stories page</a>, I noticed that the page was performing too many SQL queries. The page itself is quite simple.</p>
<img src="https://rstankov-blog.s3.amazonaws.com/refactoring_story_index.png" alt="React and Graphql Optimization Story" style="width: 95%">
<p>The following is the React structure of the page:</p>
<pre><code class="language-jsx">&lt;Header&gt;
	&lt;Flex.Row&gt;
		&lt;Image&gt;
		&lt;Flex.Column&gt;
			&lt;Title /&gt;
			&lt;Meta /&gt;
			&lt;Tagline /&gt;
			&lt;StoryVoteButton showVoters={true} /&gt;
		&lt;/Flex.Column&gt;
	&lt;/Flex.Row&gt;
&lt;/Header&gt;
&lt;Layout&gt;
	&lt;Layout.Content&gt;
		&lt;StoryItem&gt;
			&lt;Flex.Row&gt;
				&lt;Flex.Column&gt;
					&lt;Title /&gt;
					&lt;Meta /&gt;
					&lt;StoryVoteButton showVoters={false} /&gt;
					&lt;Tagline /&gt;
				&lt;/Flex.Column&gt;
				&lt;Image&gt;
			&lt;/Flex.Row&gt;
		&lt;/StoryItem&gt;
	&lt;/Layout.Content&gt;
	&lt;Layout.Sidebar /&gt;
&lt;/Layout&gt;
</code></pre>
<p>For the page we have the following GraphQL query:</p>
<pre><code class="language-graphql">#import &quot;~/components/StoryHero/Fragment.graphql&quot;
#import &quot;~/components/StoryItem/Fragment.graphql&quot;

query StoriesIndexPage($cursor: String) {
  heroStory: stories(first: 1) {
    edges {
      node {
        id
        ...StoryHeroFragment
      }
    }
  }

  stories(first: 8, after: $cursor) {
    edges {
      node {
        id
        ...StoryItemFragment
      }
    }
    pageInfo {
      endCursor
      hasNextPage
    }
  }
}
</code></pre>
<p>After some debugging, I figure out the issue is the <code>StoryVoteButton</code>. It was making N+1 queries to load the voters of story items.</p>
<p>The vote button was using the following GraphQL Fragment</p>
<pre><code class="language-graphql"># FILE: ~/components/StoryVoteButton/Fragment.graphql
#import &quot;~/components/UserImage/Fragment.graphql&quot;

fragment StoryVoteButtonFragment on Story {
  id
  hasVoted
  votesCount
  voters(first: 6) {
    edges {
      node {
        id
        ...UserImage
      }
    }
  }
}
</code></pre>
<p>Notice that it loads the voters of the story. It is quite expensive, because:</p>
<ul>
<li>the connection edge doesn't batch load voters due to pagination</li>
<li>the voters query is expensive due to complex order conditions like showing current user friends first, then popular users, and so.</li>
</ul>
<p>The button had two variants with and without voters:</p>
<img src="https://rstankov-blog.s3.amazonaws.com/refactoring_story_vote_with_voters.png" alt="React and Graphql Optimization Story">
<img src="https://rstankov-blog.s3.amazonaws.com/refactoring_story_vote.png" alt="React and Graphql Optimization Story">
<p>Showing voters was controlled by a prop named <code>showVoters</code>:</p>
<pre><code class="language-jsx">&lt;StoryVoteButton story={story} showVoters={true} /&gt;
&lt;StoryVoteButton story={story} showVoters={false} /&gt;
</code></pre>
<p>When <code>showVoters</code> is false, we don't need <code>voters</code>. However, we still load them. We only need them once on a page, so we over-fetch most of the time.</p>
<h2 id="fix1">Fix 1</h2>
<p>My first fix took me just 15 minutes.</p>
<p>I just created a second fragment, <code>FragmentWithVoters.graphql</code>, and let the component consumers use it when they pass <code>showVoters={true}</code>. This fragment included the voters.</p>
<pre><code class="language-graphql"># FILE: &quot;~/components/StoryVoteButton/Fragment.graphql&quot;
fragment StoryVoteButtonFragment on Story {
  id
  hasVoted
  votesCount
}
</code></pre>
<pre><code class="language-graphql"># FILE: &quot;~/components/StoryVoteButton/FragmentWithVoters.graphql&quot;
#import &quot;~/components/StoryVoteButton/Fragment.graphql&quot;
#import &quot;~/components/UserImage/Fragment.graphql&quot;

fragment StoryVoteButtonWithVotersFragment on Story {
  id
  voters(first: 6) {
    edges {
      node {
        id
        ...UserImage
      }
    }
  }
  ...StoryVoteButtonFragment
}
</code></pre>
<p>It was nice and easy.</p>
<p>Job well done ...  ?</p>
<p>...then, while I was doing a self-code-review, I realized that this makes the code more complex.</p>
<p>The consumer has to know that a property requires a particular fragment and use it instead of the default one. This distinction isn't obvious. ?</p>
<p>Those thoughts helped me realize and I had broken this button on the stories show page. ?</p>
<h2 id="fix2">Fix 2</h2>
<p>I moved my pull request into a &quot;work in progress&quot; state and started refactoring.</p>
<p>I decided to split <code>StoryVoteButton</code> into:</p>
<ol>
<li><code>StoryVoteButton</code> - loads only counters</li>
</ol>
<img src="https://rstankov-blog.s3.amazonaws.com/refactoring_story_vote.png" alt="React and Graphql Optimization Story">
<ol start="2">
<li><code>StoryVoteButtonWithVoters</code> - <code>StoryVoteButton</code> + story voters</li>
</ol>
<img src="https://rstankov-blog.s3.amazonaws.com/refactoring_story_vote_with_voters.png" alt="React and Graphql Optimization Story">
<h2 id="conclusion">Conclusion</h2>
<p>Moral of the story: Alway do a self-code-review as if you didn't write this code ?</p>
<p>Secondary learning is that in React, sometimes props are just hidden components, and we shouldn't push too much logic in a single component. Better to have two components. ?</p>
</div>]]></content:encoded></item><item><title><![CDATA[My Manager Journal]]></title><description><![CDATA[One of my secrets tools that help me be a good engineering manager. It helps me be focused, give feedback on my team and be on top of things.]]></description><link>https://blog.rstankov.com/my-manager-journal/</link><guid isPermaLink="false">6032b41ddaacdd0004a5662b</guid><category><![CDATA[Management]]></category><category><![CDATA[product hunt]]></category><category><![CDATA[productivity]]></category><category><![CDATA[process]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Wed, 24 Feb 2021 13:10:25 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1522794338816-ee3a17a00ae8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MXwxMjc2N3wwfDF8c2VhcmNofDE5fHxqb3VybmFsfGVufDB8fHw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1522794338816-ee3a17a00ae8?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MXwxMjc2N3wwfDF8c2VhcmNofDE5fHxqb3VybmFsfGVufDB8fHw&ixlib=rb-1.2.1&q=80&w=1080" alt="My Manager Journal"><p>I've been keeping a <a href="https://blog.rstankov.com/my-journaling-process/">personal journal</a> for about 7 years now. Four months ago, I decided to start a dedicated &quot;Manager Journal&quot; to keep track of my work at <a href="https://www.producthunt.com/">Product Hunt</a>. It helps me quite a bit in navigating the <a href="https://www.producthunt.com/stories/product-hunt-s-new-ceo-josh-buckley">big company</a> <a href="https://www.producthunt.com/stories/product-hunt-s-new-gm-ashley-higgins">changes</a> and the <a href="https://angel.co/company/product-hunt/jobs/1037478-senior-full-stack-engineer-at-product-hunt-remote">team growth</a> we've had in the last couple of months.</p>
<h2 id="process">Process</h2>
<p>I have a <a href="https://blog.bear.app/2017/09/bear-tips-pin-notes-to-the-top-to-stay-on-task/">pinned note</a> in <a href="https://bear.app/">Bear</a> and fill it in with data during the week.</p>
<p>Every Friday:</p>
<ul>
<li>Send my weekly feedback to my direct reports</li>
<li>Write in our <code>#engineering</code> Slack channel to recap all the &quot;Code improvements&quot; done by the team
<ul>
<li><em>(as the PH team grows, this might become an internal newsletter)</em></li>
</ul>
</li>
</ul>
<p>Every Sunday:</p>
<ul>
<li>Review what happened during the week</li>
<li>Archive and reset the contents of the note</li>
<li>Plan for the next week</li>
</ul>
<h2 id="template">Template</h2>
<p>I've been using this template since the beginning of November. I still tweak things here and there, but it has been fairly stable for a while now.</p>
<pre><code># ? Product Hunt Week

Start: 00/00/2021
End: 00/00/2021

## ? What I did last week
* [copied from &quot;What I worked on this week&quot; from last week&quot;]

## ? What I plan to work on week
* [what I expect to work this week]

## ✅ What I worked on this week
* [what I actually worked on this week]

## ⛑ Concerns
* 

## ? Code improvements
*  

## ? Notable events
* 

## ? Team feedback

### Me
* Good: 
* To improve:
* Observation: 

### [Teammate 1]
* Good: 
* To improve:
* Observation: 

### [Teammate 2]
* Good: 
* To improve:
* Observation: 
</code></pre>
<h2 id="details">Details</h2>
<p>Let's get into some details about this format. ?</p>
<p>The first 3 sections - &quot;last week&quot;, &quot;planned this week&quot;, &quot;done this week&quot; help me focus on what to work on during the week, what I have committed to, and what I did in reality.</p>
<p>The items in the list are very high level. Like &quot;Hiring&quot;, &quot;Improve CI performance&quot;, &quot;Audit our post scheduling system.&quot;</p>
<p>Keeping last week visible also helps me notice initiatives spanning multiple weeks or things that I'm stuck on.</p>
<p>&quot;⛑ Concerns&quot; is where I write about all my worries during the week. I got fairly good at recognizing when I'm anxious about something.</p>
<p>Examples are &quot;Project X is late&quot;, &quot;Public API performance is dropping&quot;, &quot;People are confused about X process change&quot;, &quot;We don't have good enough tools for e2e tests&quot;.</p>
<p>I often use those as a starting point for new initiatives and addressing core issues. Often, just writing those down makes me feel better.</p>
<p>&quot; ? Code improvements&quot; is the log of small system improvements done during the week by my team.</p>
<p>One of my team's core values is constant self-improvement. As part of the daily work, every engineer is nudged to extend our system while working on various features.</p>
<p>Those improvements can be small like &quot;extract a new utility for number formatting&quot; or bigger like &quot;changed moment.js to date-fns&quot;. We have a special section in our Pull Request template to list such changes.</p>
<p>Every Friday, I share those with the whole team. It has the following benefits:</p>
<ul>
<li>Not everyone keeps track of those changes, and it is useful to know</li>
<li>Shows how much progress we have made in the week</li>
<li>It is a positive reinforcement of team values</li>
</ul>
<p>&quot;? Notable events&quot; is a fairly new section. It is still in the &quot;testing phase&quot; for stuff not applicable in the other sections. Like &quot;X launched on PH, and this lead to a lot of traffic from Y&quot;, &quot;We launched Y feature&quot;, &quot;The company switched from Trell to Asana&quot;</p>
<p>&quot;? Team feedback&quot; is the section this document actually started from.</p>
<p>I keep a log for everyone in my team, <em>including me</em>.</p>
<p>I have 3 sections there - &quot;Good&quot;, &quot;To improve&quot; and &quot;Observation&quot;.</p>
<p>I'm a big user of positive reinforcement because it is easy for people to continue doing things they are already doing.</p>
<p>For &quot;To improve.&quot;, I try to focus on &quot;action -&gt; outcome&quot; and how it could have been done better. Often this is a discussion.</p>
<p>&quot;Observations&quot; are neutral or fun things I notice during the week.</p>
<p>I share this feedback with my team, and feedback tends to be small and actionable. <em>(I have a draft post about giving feedback)</em></p>
<p>It helps me spot behavior patterns, positive and negative, which I can later discuss in my monthly 1on1s.</p>
<p>I keep the feedback from myself, which comes from my team or observations about things I could have done better. It is surprisingly helpful.</p>
<p>Example - &quot;I'm not clear enough with my requirements to the DevOps. For 3 weeks in a row, I've been getting some questions about naming and environment variables.&quot;</p>
<h2 id="conclusion">Conclusion</h2>
<p>When working, it is far too easy to focus on daily activities and lose track. The &quot;Manager Journal&quot; helps me keep track of my week and helps me spot patterns across multiple weeks. It is almost passing the habit threshold for me.<br>
If you have any questions or comments, you can ping me on <a href="https://twitter.com/rstankov">Twitter</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Measuring the Output of Remote Teams]]></title><description><![CDATA[Many people wonder, "How can I know my people are working", after switching to remote. It is the wrong question. We should measure people's output, not input. But how todo so?]]></description><link>https://blog.rstankov.com/determine-if-people-working-in-remote-environment/</link><guid isPermaLink="false">60294424da7983000472b326</guid><category><![CDATA[Management]]></category><category><![CDATA[remote]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Tue, 16 Feb 2021 19:23:07 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1501516069922-a9982bd6f3bd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MXwxMjc2N3wwfDF8c2VhcmNofDN8fG1lYXN1cmV8ZW58MHx8fA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1501516069922-a9982bd6f3bd?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MXwxMjc2N3wwfDF8c2VhcmNofDN8fG1lYXN1cmV8ZW58MHx8fA&ixlib=rb-1.2.1&q=80&w=1080" alt="Measuring the Output of Remote Teams"><p>Last year the whole world was forced to switch to working from home. It was an acceleration of the trend of working remotely. One of the most common questions managers ask when this switch happened: &quot;How can I know my people are working&quot;. A lot of them have the wrong (in my opinion) assumption that:</p>
<ol>
<li>If you are in the office, you are working</li>
<li>People are always searching for a way not to work.</li>
</ol>
<p>&quot;Are people working&quot; is the wrong question. The right question is, &quot;are we delivering&quot; and &quot;are we working on the right thing&quot;. I don't care if the engineers on my team only work 1-2 hours a day, as long as they deliver.</p>
<p>In an office environment, we are often blinded by the input we see - for example, &quot;Rado is always in front of his laptop, he must be working a lot&quot;. Even worse, if someone is very &quot;loud&quot; during standups and meetings - they must be very knowledgeable.</p>
<p>The usual advice people give is to &quot;Measure output, not input&quot;.</p>
<p>However, this isn't very actionable. What is the output? What is the correct output? Do I get the output of an individual or a team?</p>
<h2 id="preconditions">Pre-conditions</h2>
<p>I have found out that to measure the output of a team, you need the following pre-conditions:</p>
<ul>
<li>Have an anchor or baseline</li>
<li>Create a rhythm of work for everyone - all sprints start and finish on the same date.</li>
<li>Have  clear expectations about the expected output</li>
<li>Enable people to work independently by removing blockers and dependencies between people (<a href="https://blog.rstankov.com/collaborative-single-player-mode/">collaborative single-player mode</a>)</li>
</ul>
<p>Not having clear expectations is easier to hide when you're in the same office because people assume other people know the context.</p>
<h2 id="howthiscurrentlyworksatproducthunt">How this currently works at Product Hunt</h2>
<p>In <a href="https://www.producthunt.com/">Product Hunt</a>, people are grouped into teams, and they work in 2-week sprints. Every team sprint starts and ends at the same time. This creates a rhythm of work.</p>
<p>Tasks in the sprint are split in such a way that developers can work in <a href="https://blog.rstankov.com/collaborative-single-player-mode/">single-player mode</a>.</p>
<p>We encourage people to open pull-requests and deploy to production every 1~2 days with feature flags. A single task can have 2-3 or more pull-requests.</p>
<p>We have weekly check-in on sprint progress - is it on track or not.</p>
<p>With this process, it is easy to spot if a task is stuck and possible to quickly correct course. I have very rarely seen cases where someone is not working. Usually, the task is stuck because of a scope issue or technology challenge.</p>
<h2 id="overwork">Overwork</h2>
<p>What I have often seen in a remote environment is people overworking. Because I don't know if someone works 2 hours or 12, it is easy to hide overwork. This makes it difficult for this individual to set the right expectations and it hides problems with task scoping.</p>
<p>The way to solve this is to trust the team. Don't blame people when they are late, and don't award people who overwork.</p>
<p>I often say:</p>
<blockquote>
<p>It is not a problem to be late. A problem is - not telling me you will be late the moment you realize it.</p>
</blockquote>
<h2 id="conclusion">Conclusion</h2>
<p>I have been working remotely for about a decade, and I can't see myself ever measuring people's hours. I care that people don't do many extra hours, though, because this is unhealthy and stems from bad planning.</p>
<p>Being in an office often hides problems with the process. You need 10x more process for remote sensing initially, but, with time, it scales better than being at the same place.</p>
<p>If you have any questions or comments, you can ping me on <a href="https://twitter.com/rstankov">Twitter</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Structuring React Components]]></title><description><![CDATA[How I structure, React components at Product Hunt. The following component a directory pattern and optimizing for scanability.]]></description><link>https://blog.rstankov.com/structuring-react-components/</link><guid isPermaLink="false">600dd49ef04851000432565a</guid><category><![CDATA[React]]></category><dc:creator><![CDATA[Radoslav Stankov]]></dc:creator><pubDate>Wed, 27 Jan 2021 15:11:57 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1587620962725-abab7fe55159?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MXwxMjc2N3wwfDF8c2VhcmNofDl8fGNvZGV8ZW58MHx8fA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://images.unsplash.com/photo-1587620962725-abab7fe55159?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MXwxMjc2N3wwfDF8c2VhcmNofDl8fGNvZGV8ZW58MHx8fA&ixlib=rb-1.2.1&q=80&w=1080" alt="Structuring React Components"><p>I've been using React since 2016. React has changed a lot since then, but the fundamentals have stayed the same.</p>
<p>In the last couple of years, the <a href="https://www.producthunt.com/">Product Hunt</a> team and I have settled on a way to structure our components.</p>
<p>The goal is to make understanding components easier.</p>
<p>Every component should provide quick answers to the following questions.</p>
<ul>
<li>What are the props this component needs?</li>
<li>What does this component do?</li>
<li>What are the component's states?</li>
<li>What is the component's logic?</li>
<li>How can we trace the logic?</li>
<li>Is it easy to refactor?</li>
</ul>
<h2 id="componentasdirectory">Component as directory</h2>
<p>Components are encapsulated in directories that contain their individual parts.</p>
<p>The only public thing there is the main <code>index</code> file.<br>
Everything else is considered private.</p>
<p>Here is what the component directory looks like:</p>
<pre><code>~/components/[Component]/index.tsx                // default export is the used component
~/components/[Component]/Fragment.graphql         // GraphQL fragment for the component
~/components/[Component]/Mutation.graphql         // GraphQL mutation for the component
~/components/[Component]/styles.module.css        // css-module, with styles for the component
~/components/[Component]/[icon].svg               // icon asset
~/components/[Component]/utils.ts                 // helper functions/classes for the component
~/components/[Component]/utils.test.ts            // tests for helper functions/classes
~/components/[Component]/[SubComponent]/index.tsx // when this component is complex, it's might
~/components/[Component]/[SubComponent]/...       // be split it into multiple components
~/components/[Component]/[SubComponent]/...       // they are private and shouldn't be imported
~/components/[Component]/[SubComponent]/...       //  from outside its parent component
</code></pre>
<h2 id="component">Component</h2>
<p>Every component we have, has an entry point like the following:</p>
<pre><code class="language-js">// components/Component/index.js

// 1) Component props
// always name this &quot;IProps&quot;
// this makes it easy to quickly see the expected props for this component
interface IProps {
  // ...
};

// 2) Main component export
// immediately after, put the component name since this is what this package is all about
// use default function values for default props
export default function ComponentName({ prop1, prop2 = 'default value' }: IProps) {
  // component is structured by
  // 2.1) Hooks
  // if this is more than 5 lines, a custom hook is extracted
  // more about this - https://blog.rstankov.com/extract-react-hook-refactoring/

  // 2.2) Guard clauses
  if (someCondition) {
    return null;
  } 

  if (someOtherCondiation) {
    return &lt;SamePlaholder /&gt;
  }

  // 2.3) The happy path of the component
	return &lt;div&gt;My component&lt;/div&gt;;
}

// 3) Everything else...
// Here's where the smaller helper hooks, utils, and components live.
// If those get bigger, they can be moved into separate files in the component directory
</code></pre>
<p>This structure makes it very easy to scan component and find what you are looking for.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This structure has served us well, and its fundamentals have stayed the same when migrating from classes to hooks, from flow type to TypeScript, and so.</p>
<p>If you have any questions or comments, you can ping me on <a href="https://twitter.com/rstankov">Twitter</a>.</p>
</div>]]></content:encoded></item></channel></rss>