<?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: Scott Dudley</title>
    <description>The latest articles on DEV Community by Scott Dudley (@dudleysr).</description>
    <link>https://dev.to/dudleysr</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%2F82424%2Fafc53c76-b1bc-4222-9b40-e16d479469b6.jpeg</url>
      <title>DEV Community: Scott Dudley</title>
      <link>https://dev.to/dudleysr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dudleysr"/>
    <language>en</language>
    <item>
      <title>Ranked_Model: A Rails Gem for Multiple Data Sets</title>
      <dc:creator>Scott Dudley</dc:creator>
      <pubDate>Tue, 30 Oct 2018 23:03:22 +0000</pubDate>
      <link>https://dev.to/dudleysr/rankedmodel-a-rails-gem-for-multiple-data-sets-20bd</link>
      <guid>https://dev.to/dudleysr/rankedmodel-a-rails-gem-for-multiple-data-sets-20bd</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhglziu1h8hsdgrxpdrc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhglziu1h8hsdgrxpdrc.png" alt="Ranked_Model" width="800" height="394"&gt;&lt;/a&gt;&lt;br&gt;
Recently we ran into an issue on a client Rails application where we wanted to customize the order in which certain items would appear on various pages. The admin users would log in to a CMS built with &lt;a href="https://github.com/activeadmin/activeadmin" rel="noopener noreferrer"&gt;&lt;code&gt;active_admin&lt;/code&gt;&lt;/a&gt;, create a widget with information about a company, and then either manually set the position in the list while creating or editing the widget, or use a 'move up and down' feature on the index page. &lt;/p&gt;

&lt;p&gt;Originally, we utilized the &lt;a href="https://github.com/swanandp/acts_as_list" rel="noopener noreferrer"&gt;&lt;code&gt;acts_as_list&lt;/code&gt;&lt;/a&gt; gem to implement this feature as it allowed us to quickly and painlessly add all the functionality we needed.&lt;/p&gt;

&lt;p&gt;I ran into an issue, though, when our client requested that each widget have two separate rankings, depending on which page the user was visiting. If the user was on the Foo product page, the widget might appear at position 2, but on the Bar page the widget would render in position 3. &lt;/p&gt;

&lt;p&gt;My initial instinct was to create a new &lt;code&gt;position&lt;/code&gt; attribute on the widget model and pass in both the new attribute and the existing position column into the &lt;code&gt;acts_as_list&lt;/code&gt; method. However, I hit a roadblock since &lt;code&gt;acts_as_list&lt;/code&gt; is not capable of handling two different attributes on a single model.&lt;/p&gt;

&lt;p&gt;This was a time sensitive request and as I dove into the possible solutions of using associations and join tables to add this feature with &lt;code&gt;acts_as_list&lt;/code&gt;, I couldn't help but feel that there should be a quicker solution that could handle having two different ranking systems for a single model.&lt;/p&gt;

&lt;p&gt;My search led me to discover &lt;a href="https://github.com/mixonic/ranked-model" rel="noopener noreferrer"&gt;&lt;code&gt;ranked_model&lt;/code&gt;&lt;/a&gt;, a gem with a similar goal to &lt;code&gt;acts_as_list&lt;/code&gt; but with a different implementation and functionality, most of which fit our criteria with much more ease than the &lt;code&gt;acts_as_list&lt;/code&gt; approach.&lt;/p&gt;

&lt;p&gt;After installing the gem and including RankedModel in the class file, you can simply declare which attributes will be handled by &lt;code&gt;ranked_model&lt;/code&gt; with the following lines:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ranks :foo&lt;/code&gt;&lt;br&gt;
&lt;code&gt;ranks :bar&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This would work fine on a greenfield project or a new attribute but I was adding this to an attribute that already had position-oriented ranking applied. Both gems use integers as their data types, but for the most part, the similarities end there.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;acts_as_list&lt;/code&gt; uses sequential numbers in its assigned attribute, incrementing and decrementing multiple objects as necessary with each change in the order. For example by taking a list of widgets with positions of A: 1, B: 2, C: 3, &amp;amp; D: 4, then moving D to the first position, D would become 1 but B, C, and A would all be incremented to fully reorder the list. There are more methods to run each time but it’s simple to glance at any given object and understand its exact position.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Ranked_model&lt;/code&gt; on the other hand, utilizes relatively large numbers, both positive and negative, to more quickly and efficiently order objects. Creating the same 4 objects with ranked_model looks far different, that acts_as_list, being more like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;A: -2342567, B: -1437689, C: 1289078, D: 2165876&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Since ranked_model generally deals with larger integers, it can cause more difficulty in development and debugging, particularly in the console. Once a sizable number of objects is reached, finding the relative place in the ranking from only its position attribute would require  notable attention to detail. However, calling a method to actually move these positions is simple enough. &lt;/p&gt;

&lt;p&gt;To mimic the same widget repositioning as the acts_as_list example, moving D to the first position, the code would require a call of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_attribute&lt;/span&gt; &lt;span class="ss"&gt;:foo_ranking_position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:first&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which would result in something like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;D: -2465876, A: -2342567, B: -1437689, C: 1289078&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So you can see, using these large integers allowed us to reorder D to the front and only modify a single object! This becomes especially valuable in situations with large datasets. As mentioned earlier, ranked_model also allows us to call rankings on multiple attributes, so using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_attributes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;foo_ranking_position: :first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;bar_ranking_position: :last&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;would be completely acceptable.&lt;/p&gt;

&lt;p&gt;Migrating from acts_as_list to ranked_model did take a little bit of work, though. Since we already had an ordered attribute from acts_as_list, attempting to just write in and start using ranked_model on it outright led to some strange interactions and incorrect sorting. My solution was to add the following to a database migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;reversible&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;up&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:foo_position&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_attribute&lt;/span&gt; &lt;span class="ss"&gt;:foo_position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:foo_position&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_attribute&lt;/span&gt; &lt;span class="ss"&gt;:bar_position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is probably a little overkill and almost certainly could be refactored to look more elegant or efficient (and also should probably be a rake task) but it worked with our need for a quick turnaround. It's essentially going through our original order from &lt;code&gt;acts_as_list&lt;/code&gt; and telling &lt;code&gt;ranked_model&lt;/code&gt; to order it at the end of the list, making sure all positions now have a ranked_model compatible position.&lt;/p&gt;

&lt;p&gt;After using some of the syntax particular to the gem, in order to modify the methods and controllers that were updating position in the class file, ranked_model proved to be an efficient way to sort and rank multiple datasets for Rails.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Estimate Small Tasks for a Big Client</title>
      <dc:creator>Scott Dudley</dc:creator>
      <pubDate>Wed, 05 Sep 2018 22:05:25 +0000</pubDate>
      <link>https://dev.to/dudleysr/how-to-estimate-small-tasks-for-a-big-client-31jf</link>
      <guid>https://dev.to/dudleysr/how-to-estimate-small-tasks-for-a-big-client-31jf</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw2dj4fyar5o54770vsah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw2dj4fyar5o54770vsah.png" alt="Estimating Tasks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s a plethora of articles, books, and blogs on how to accurately estimate large software projects. Something that’s going to take multiple developers a decent chunk of a week, month, or year will almost always have meetings between developers, project managers, and other staff where estimates will be checked and challenged.&lt;/p&gt;

&lt;p&gt;Where I have definitely struggled to improve, though, has been estimating the all-too-common ‘small’ tickets: ones that are large enough that an estimate is expected but too small to necessitate bringing in additional help or conferring with other developers.&lt;/p&gt;

&lt;p&gt;For me these are tickets typically are somewhere in the 4-15 hour range. Anything less is likely a simple enough solution to go on gut instinct, and anything more might affect my schedule for the week, so I’d likely need to run it by someone else. &lt;/p&gt;

&lt;p&gt;Here are some tips and tricks I’ve found help me independently estimate this type of task&lt;/p&gt;

&lt;h1&gt;
  
  
  Compare to Completed Tickets
&lt;/h1&gt;

&lt;p&gt;This one is a bit obvious, but if you can compare the current task to a completed task, you can get a really great baseline with which to work. For example, if a client is looking to mimic the analysis of another report but reference a new set of data, seeing how many hours were put into the ticket regarding the original report feature can help give you a ballpark.&lt;/p&gt;

&lt;p&gt;This also speaks to the importance of labeling and linking tickets and issues. Institutional knowledge of a project and specific tickets is great for experienced developers but without some sort of trail or documentation of past work, newer developers will have a hard time knowing that previous efforts might be applicable to new problems. While it can feel tedious, be sure to fully learn and utilize all the various tools of your ticketing or project management system.&lt;/p&gt;

&lt;h1&gt;
  
  
  Think Work Days, not Hours
&lt;/h1&gt;

&lt;p&gt;Sometimes trying to assign specific hours to a task can be daunting. Will figuring out this specific loop I know I’ll need take an hour? Three? Maybe three and a half? Estimating specific elements of a ticket can start feeling especially nebulous and arbitrary, which can easily lead to spinning your wheels while estimating.&lt;/p&gt;

&lt;p&gt;When I find myself in that situation I’ll often ask myself the following question:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If I was given an entire work day, am I confident I could get this task done?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This question can help set the boundaries for tickets. If your answer is a quick and resounding ‘yes’ then you know the estimate can be narrowed down to somewhere in the 2-8 hour range. If your answer is a resounding ‘no’ then it’s likely time to talk with a project manager or at least take some time to figure out why exactly this will be a multi-day task.&lt;/p&gt;

&lt;p&gt;If your answer is a less solid ‘yes’ or a ‘maybe’, some other considerations should come into play. Context switching is likely to become an issue in these instances as urgent bugs and meetings are extremely likely to interrupt your work in this type of ticket. When I am estimating a ticket with a ‘maybe’ on completion in a single work day, I tend to add about 10-20% more time on whatever my final estimate is purely for context switching. That way stepping away from a ticket does not have any additional pressure to try and make up for the time from context switching.&lt;/p&gt;

&lt;h1&gt;
  
  
  Rubber Ducky Your Pseudo Code
&lt;/h1&gt;

&lt;p&gt;This is a way I double-check my level of confidence in an estimate. If I can write down my pseudo code and then talk my way through how that pseudo code will complete the requirements, I can feel confident that my estimate will be accurate. If I have trouble talking out the whole process or feel shaky about where exactly certain parts of the solution would fit, then I know I should add some padding to my estimate.&lt;/p&gt;

&lt;p&gt;I recently had an example where this method led me to more accurately estimate a ticket. I initially estimated a ticket taking four hours based on previous similar tickets but I decided to step back and actually write out some pseudo code for the solution. As I walked through the process in my head, I floundered a bit on whether a particular part of the planned code should live in a controller or the model. I bumped the estimate on the ticket to five hours because of my hesitation and the final tally of time spent on the issue was four hours and forty seven minutes.&lt;/p&gt;

&lt;h1&gt;
  
  
  Retrospectives
&lt;/h1&gt;

&lt;p&gt;Whenever I go ten percent or more over my estimate on a ticket, I try to take some time and figure out why I underestimated it. Was it a communication issue? Did I not fully understand the requirements? Did I forget to include time for writing tests? Was context switching or catching up on project changes an issue? Maybe I just plain old thought one specific method would not be as complex as expected.&lt;/p&gt;

&lt;p&gt;Estimating is a skill and skills take time, effort, and experience to improve. Taking time and really digging into the specific causes of an inaccurate estimate can sharpen your estimating skills dramatically.&lt;/p&gt;

&lt;h1&gt;
  
  
  Gut Instinct and Willingness to Fail
&lt;/h1&gt;

&lt;p&gt;In the end, your gut instinct is often one of the most important parts of estimating tickets. Sometimes a feature is entirely new and does not have a relatable, completed ticket. Maybe it will require interacting with a newer framework, so you are unsure if something will take a day or a week! Those kind of situations can be especially nerve-wracking as estimating those can feel like exposing yourself to a situation where you will be way off base. The reality is that you likely will be from time to time. Remember that learning from mistakes is always one of the best ways to improve at something!&lt;/p&gt;

&lt;h1&gt;
  
  
  All the Pieces Matter
&lt;/h1&gt;

&lt;p&gt;Estimating small tickets may not seem to be as essential or dramatic as estimating large projects, but consistently giving more accurate estimates for large numbers of small tickets can be a major boon for both developers and clients. Being half an hour to an hour more accurate on small tickets can quickly add up to significant numbers. &lt;/p&gt;

&lt;p&gt;It gives clients more confidence in what they can tell their stakeholders to expect and it allows for better forecasting and resourcing for developers.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>projectmanagement</category>
    </item>
  </channel>
</rss>
