<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Steve B</title>
    <description>The latest articles on DEV Community by Steve B (@meadsteve).</description>
    <link>https://dev.to/meadsteve</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F447949%2F18af4f04-a307-4954-af8b-102c88f8d6b3.png</url>
      <title>DEV Community: Steve B</title>
      <link>https://dev.to/meadsteve</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/meadsteve"/>
    <language>en</language>
    <item>
      <title>Leaving kind (and productive) PR feedback</title>
      <dc:creator>Steve B</dc:creator>
      <pubDate>Mon, 22 Feb 2021 07:29:46 +0000</pubDate>
      <link>https://dev.to/meadsteve/leaving-kind-and-productive-pr-feedback-km2</link>
      <guid>https://dev.to/meadsteve/leaving-kind-and-productive-pr-feedback-km2</guid>
      <description>&lt;p&gt;(This post is originally from &lt;a href="https://blog.meadsteve.dev/team-work/2021/02/21/leaving-kind-pr-feedback/"&gt;https://blog.meadsteve.dev&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;After responding a tweet about how to review PRs in a kind way I started thinking about how I try and review PRs myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;p&gt;Before I start discussing how I try to leave more constructive feedback I'm going to state some assumptions I've made.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing and linting is automated
&lt;/h3&gt;

&lt;p&gt;Humans make lousy proofreaders compared to a machine. I'm assuming issues of styling and obvious bugs don't require a comment.&lt;/p&gt;

&lt;h3&gt;
  
  
  We're working in a team environment with richer forms of communication than a PR
&lt;/h3&gt;

&lt;p&gt;This post assumes a team working on a project. Where there can be face to face meetings, video calls, pairing, mobbing&lt;br&gt;
or any other activity like this. For open source a lot of what I'm going to write doesn't really apply as async PRs are the main way of communicating.&lt;/p&gt;

&lt;h3&gt;
  
  
  PRs are often too late in the process for major changes.
&lt;/h3&gt;

&lt;p&gt;For major changes we should try to pair, discuss or plan the issue before committing time to a large code change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Most of the time it's better to move forward, learn something, then refactor
&lt;/h3&gt;

&lt;p&gt;I like iterative development. Unless there's a major issue with the code or architecture I'd rather move forward, learn from it and apply &lt;br&gt;
some refactoring after.&lt;/p&gt;

&lt;h3&gt;
  
  
  We're often more emotionally invested in our code that we'd like to admit
&lt;/h3&gt;

&lt;p&gt;Once time has been spent on a change it's very hard to not be committed to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  PRs are a learning opportunity for the reviewer(s) too
&lt;/h3&gt;

&lt;p&gt;I'll often ask questions to understand the code a little better. I treat this as a learning opportunity that &lt;em&gt;may&lt;/em&gt; help with the review but&lt;br&gt;
doesn't have to,&lt;/p&gt;

&lt;h2&gt;
  
  
  How I review
&lt;/h2&gt;

&lt;h3&gt;
  
  
  First Pass
&lt;/h3&gt;

&lt;p&gt;The first thing I want to look for is potential security issues, major design flaws or bugs. If I find anything like this I'll want to flag the PR as one that needs work before it can be merged. Next I'll look for areas I don't really understand. These are good places for me to ask questions. This might be because I'm unfamiliar with the area of the code. In which case it's a good place for me to learn. Other times it's because something about how we're changing the code is unclear. This can often highlight the need for some documentation or maybe some changes in the code to make it more self documenting. &lt;/p&gt;

&lt;h3&gt;
  
  
  Second pass
&lt;/h3&gt;

&lt;p&gt;If there were no major problems or areas I didn't understand from my first pass I'll consider the PR approved and ready for merge. Now I'll look through the code for oppurtunities for improvements. I don't think it's productive to comment on every line of a PR so after going over the whole PR I'll pick a few areas I've found that would have the biggest impact on code quality. This is where I leave my feedback, preferably in the form of concrete suggestions. I try and make it clear that these are suggestions.&lt;/p&gt;

&lt;h4&gt;
  
  
  An exception to the above for RFC(Request For Comments) style PRs
&lt;/h4&gt;

&lt;p&gt;A lot of teams I've worked in have a pattern where some more experimental changes will be made with a small spike implementation. This acts as a concrete example to base discussion around. In these cases I will provide a lot more feedback as this is the whole purpose of the PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  What if we keep finding large bugs, or areas that need discussing
&lt;/h2&gt;

&lt;p&gt;My preference is for the "light touch PRs" I described in my second pass. If we find that we're often having complex discussions around how a PR should look or a lot of bugs are being highlighted then this is a sign to me that some part of our earlier process is not right. Normally the best way to deal with this is examining the kinds of problems in a retrospective. The underlying cause is often some combination of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We're missing some test coverage or scaffolding so bugs aren't caught automatically.&lt;/li&gt;
&lt;li&gt;There's a lack of shared understanding in the team around an area in the code.&lt;/li&gt;
&lt;li&gt;There's a disagreement in the team about the best way of solving a problem&lt;/li&gt;
&lt;li&gt;The design goals for the project are unclear or implict and undocumented.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Do I always follow this?
&lt;/h2&gt;

&lt;p&gt;No. Not strictly. Each team, situation and contributor is different and I'll try and adapt to that. Sometimes I'll do more written feedback in a PR, sometimes I'll do more outside a PR. Sometimes I just get it wrong. What I've described is my ideal and my starting point.&lt;/p&gt;

</description>
      <category>codequality</category>
      <category>continousimprovement</category>
      <category>github</category>
      <category>feedback</category>
    </item>
    <item>
      <title>Yielding for testability in python</title>
      <dc:creator>Steve B</dc:creator>
      <pubDate>Fri, 01 Jan 2021 18:48:23 +0000</pubDate>
      <link>https://dev.to/meadsteve/yielding-for-testability-in-python-1j20</link>
      <guid>https://dev.to/meadsteve/yielding-for-testability-in-python-1j20</guid>
      <description>&lt;p&gt;(This is a crosspost from my blog: &lt;a href="https://blog.meadsteve.dev/programming/2021/01/01/yielding-for-testability/"&gt;https://blog.meadsteve.dev/programming/2021/01/01/yielding-for-testability/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I was writing a small cli tool and wanted to try out &lt;a href="https://typer.tiangolo.com/"&gt;typer&lt;/a&gt;.&lt;br&gt;
I'm a big fan of fast api, so I thought a cli library from the same author was worth a look.&lt;br&gt;
This blog post will cover a pattern I ended up using to help with testing.&lt;/p&gt;
&lt;h2&gt;
  
  
  The app and the first attempt at tests
&lt;/h2&gt;

&lt;p&gt;I won't go into too much detail about my actual problem, but I ended up with something like the &lt;br&gt;
following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;typer&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Starting up"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Do the first thing
&lt;/span&gt;    &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Result of first thing was X"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Do the second  thing
&lt;/span&gt;    &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Result of second thing was X"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Do some clean up
&lt;/span&gt;    &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"All done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value in the code I was writing was the output of &lt;code&gt;typer.echo&lt;/code&gt;. So I wanted to test it.&lt;br&gt;
Initially in my tests I was monkey patching &lt;code&gt;typer.echo&lt;/code&gt; and asserting that it matched what&lt;br&gt;
I wanted. This was okay, but I don't like monkey patching with mocks as it often becomes &lt;br&gt;
very tangled and complicated.&lt;/p&gt;
&lt;h2&gt;
  
  
  A solution? Yielding.
&lt;/h2&gt;

&lt;p&gt;Since I already had a function representing my command invocation I thought it would be&lt;br&gt;
really nice if I could just return the output. Then I could call the function and assert&lt;br&gt;
that it had output what I wanted. However, given that quite significant delays could&lt;br&gt;
happen in between each of my echo statements I didn't really want to wait until the end&lt;br&gt;
to get all my output. So instead &lt;code&gt;yield&lt;/code&gt; seemed like a good candidate.  I updated my code&lt;br&gt;
to look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Starting up"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Do the first thing
&lt;/span&gt;    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Result of first thing was X"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Do the second  thing
&lt;/span&gt;    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Result of second thing was X"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Do some clean up
&lt;/span&gt;    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"All done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now my function was a generator. The code looked almost identical which was nice. &lt;br&gt;
I did need to add a layer on top to run this generator but this was not too complicated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;typer&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_cli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;generator_func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;output_line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;generator_func&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and now my test cases could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_something&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"first message"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"second message"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="n"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"etc."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm counting this as a success. My tests became a lot simpler which was my initial goal.&lt;br&gt;
In addition, the &lt;code&gt;my_command&lt;/code&gt; function lost its dependency on the &lt;code&gt;typer&lt;/code&gt; library and became&lt;br&gt;
agnostic to how the input is displayed to the user (though this wasn't really my initial goal).&lt;/p&gt;

</description>
      <category>python</category>
      <category>testing</category>
      <category>generators</category>
      <category>yield</category>
    </item>
  </channel>
</rss>
