Saturday, April 22, 2017

Sunk Cost Fallacy in software development

I am working on a project having to do with government certification. One of their requirements is very odd, so we've had to put time an effort into doing design and research into how we could best meet the odd requirement. Given that this is for a government certification, the requirement is set in stone (or so we thought...!). We thought we simply misunderstood their requirement, and so we figured out two design alternatives - one that was simple based on what we thought they meant, and the other based on if they truly required this weird thing.

We got clarification and they truly required the weird thing. We accepted this, and spent 2 weeks on the design / proof of concept work that takes place before actual coding. That included review meetings with the top dogs in the company. So alot of time and effort was put into this design. 

The design was approved and the stars were aligning, code was about to be slapped into an IDE. But then at the last minute the government changed their requirement (weird, that never happens in software development!). They gave us a choice - we could do the weird requirement or the smart requirement, and we'd just need to tell them what way we chose. 

So now we're at a fork in the road:
  • Go ahead with the coding on the already-completed design, even though the road ahead was a long nasty one
  • Do the simple design approach, taking a day or two to go through the approval process again, and then proceed with the very simple coding

You'd think the obvious choice would be to do the simple design approach, but that's where the Sunk Cost Fallacy comes in. Time and energy was already spent on the first design, therefore we should continue to invest in this design! Nope. The time and energy spent on the WRONG design does not justify going forward with it. 

Sunk Cost Fallacy teaches us that the smart thing to do is cut our losses, and not be fooled into thinking that previous resources spent justifies continuing to throw resources at the wrong solution.

We did end up going with the simpler design, but that's because we were aware of the Sunk Cost trap, and were able to avoid it and make the wise decision.

Saturday, April 8, 2017

Operate on subsets of arrays using LINQ

LINQ Take() and Skip() make it easy to operate on subsets of an array, or any IEnumerable.

Sample problem: I want to know if a number is a contiguous sequence of digits in ascending order

       
Sample solution using Skip() and Take()
private static bool isAsc(int tester)
        {
          char[] asc = tester.ToString().Replace("0", ":").ToCharArray();
         
           char prev = asc[0];
           foreach(char c in asc.Skip(1))
           {
             prev++;
             if(prev != c)
              return false;
           }
           return true;
        }


if i didn't have Skip() available i would have written that like this:
for(int i = 1; i < asc.Length; i++)

Now, what if I wanted to find out if a subset had the property? Let's say, I want to know if the 2nd-5th digits have the property. In this case i would use Skip() + Take()

private static bool isAsc(int tester)
        {
          char[] asc = tester.ToString().Replace("0", ":").ToCharArray();
         
           char prev = asc[0];
           foreach(char c in asc.Skip(1).Take(3))
           {
             prev++;
             if(prev != c)
              return false;
           }
           return true;
        }


Ref:
1. Code Wars kata i used Skip() on: https://www.codewars.com/kata/52c4dd683bfd3b434c000292







Friday, March 24, 2017

Improve your code and design intuition

You should be able to develop your code and design sense to the point where you can look at anyone's code (including your own) and intuitively know if it's good or not. This is a critical skill to have if you are responsible for doing code and design reviews.

Here's some suggestions based on what i've done

1. Study design patterns and antipatterns
-Read some design patterns book. I liked Head First Design Patterns
-Read AntiPatterns
-Read Refactoring


2. Study good code, code smells, and refactoring
-Read Code Complete
-Read Clean Code
-Read Refactoring
-Read Programming Pearls

One of my favorite references for this is here: https://sourcemaking.com. I like to point to this in code reviews as a teaching tool. Like "use Extract Method for this part of the code. https://sourcemaking.com/refactoring/extract-method"

3. Lots and lots of practice

-Katas (links from Clean Coder book)
http://codekata.pragprog.com 
http://katas.softwarecraftsmanship.org
http://butunclebob.com/
http://thecleancoder.blogspot.com/2010/10/craftsman-62-dark-path.html

-TopCoder algorithm challenges

-Code Wars

4. Use different languages and write different types of programs. Don't get too comfortable!
-Use C# at work? Study Python for fun
-Only do application development? Head to Udacity or freecodecamp for webdev

An excellent way to do this is to use 57 Exercises https://pragprog.com/book/bhwb/exercises-for-programmers




Saturday, February 4, 2017

Refactoring challenge - yahtzee

Refactoring challenge
http://jonjagger.blogspot.co.uk/2012/05/yahtzee-cyber-dojo-refactoring-in-java.html

My end results

                                           Original           Refactored
Maintainablility                  61                        90
Cyclometric Complexity    64                        47
Lines of Code                     157                      86

Class Coupling                    0                         11

My refactored C# version of this


using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RefactoringPractice { public enum DieValue { One =1, Two=2, Three=3, Four=4, Five=5, Six=6 } public class Dice { private static int NUM_DIE = 5; private int[] dice = new int[NUM_DIE]; private static DieValue[] dieValues = (DieValue[])Enum.GetValues(typeof(DieValue)); public Dice(int die1, int die2, int die3, int die4, int die5) { dice[0] = die1; dice[1] = die2; dice[2] = die3; dice[3] = die4; dice[4] = die5; } internal int Sum() { return dice.Sum(); } internal int SumOfNum(DieValue val) { return whereVal(val).Sum(); } private IEnumerable<int> whereVal(DieValue val) { return dice.Where(e => e == (int)val); } internal int CountOfNum(DieValue val) { return whereVal(val).Count(); } internal bool AllDieHaveNum(DieValue val) { return CountOfNum(val) == NUM_DIE; } internal static DieValue[] DieValues { get { return dieValues; } } } public static class Yatzy { public static int Chance(Dice dice) { return dice.Sum(); } public static int yatzy(Dice dice) { foreach(DieValue val in Dice.DieValues) { if (dice.AllDieHaveNum(val)) return 50; } return 0; } public static int Ones(Dice dice) { return dice.SumOfNum(DieValue.One); } public static int Twos(Dice dice) { return dice.SumOfNum(DieValue.Two); } public static int Threes(Dice dice) { return dice.SumOfNum(DieValue.Three); } public static int Fours(Dice dice) { return dice.SumOfNum(DieValue.Four); } public static int Fives(Dice dice) { return dice.SumOfNum(DieValue.Five); } public static int Sixes(Dice dice) { return dice.SumOfNum(DieValue.Six); } public static int ScorePair(Dice dice) { foreach(DieValue val in Dice.DieValues.Reverse()) { if (dice.CountOfNum(val) == 2) return (int)val * 2; } return 0; } public static int TwoPair(Dice dice) { int numPairs = 0; int score = 0; foreach(DieValue val in Dice.DieValues.Reverse()) { if(dice.CountOfNum(val) >= 2) { numPairs++; score += ((int)val * 2); } } if (numPairs == 2) return score; else return 0; } public static int FourOfAKind(Dice dice) { return OfAKind(4, dice); } private static int OfAKind(int num, Dice dice) { foreach (DieValue val in Dice.DieValues) { if (dice.CountOfNum(val) >= num) return (int)val * num; } return 0; } public static int ThreeOfAKind(Dice dice) { return OfAKind(3, dice); } private static int straight(int left, int right, Dice dice) { for (int i = left; i <= right; i++) { if (dice.CountOfNum(Dice.DieValues[i]) != 1) return 0; } return dice.Sum(); } public static int SmallStraight(Dice dice) { return straight(left: 0, right: Dice.DieValues.Length - 2, dice:dice); } public static int LargeStraight(Dice dice) { return straight(left: 1, right: Dice.DieValues.Length - 1, dice: dice); } public static int FullHouse(Dice dice) { foreach(DieValue threeKind in Dice.DieValues.Reverse()) { if(dice.CountOfNum(threeKind) == 3) { foreach(DieValue twoKind in Dice.DieValues.Reverse()) { if(dice.CountOfNum(twoKind) == 2) { return ((int)threeKind * 3) + ((int)twoKind * 2); } } } } return 0; } } }

Thursday, October 27, 2016

Open form in the UI thread from another thread

Problem
I have a thread that wakes up and processes stuff based on a schedule and it needs to show progress in a window. This is so that if any user happens to be using the system when this pops up, they can cancel it / see what's going on.

Solution - create a dummy form to Invoke()
        public static Form globalForm;
        private void btnStart_Click(object sender, EventArgs e)
        {
            globalForm = new Form();
            globalForm.Show();
            globalForm.Hide();

            Task t = new Task(Run);
            t.Start();

        }

        Test test; //this is the form i want to show when this job is running
        public void Run()
        {
            test = new Test();
         
            while (true)
            {
                    ShowTestForm();

                //do stuff

                //update status on the window
            }
        }

        private void ShowTestForm()
        {
             /* Note: I prefer this wrapped version over calling globalForm.Invoke(...) from the Run() method. I believe this is cleaner code.  */

            MethodInvoker m = () =>
            {
                if (!test.Visible)
                {
                   test.Show();
                    test.Activate();
                 };
            };
            globalForm.Invoke(m);
        }

Tuesday, September 27, 2016

Error: Subreport could not be shown

Problem
I have a report that has a table with a subreport for each record. When I try to run the report it spins for awhile and then says, "Error: Subreport could not be shown."

Solution
1. Open the parent report's .rdl so that you can see the report XML, not the design surface
2. Search for <Subreport
3. In <ReportName> put .rdl at the end of the subreport's name
4. Upload the parent report to SSRS


Troubleshooting notes
What threw me off here was the fact that it was working inconsistently. I would use the Browse... button in the designer and point to this report. It would pull back the report name without .rdl at the end. This somehow worked. Manually typing in the name in the designer and adding .rdl to the name did not work. I had to open the report in a text editor and edited the name in there.
There was an error in this gadget