воскресенье, 4 августа 2013 г.

TDD adventures Part 1. Productive TDD from real life.

Following is a real-life demonstration of how effective one may be applying TDD to solve tasks under stress conditions. 
Imagine that you are limited on time, you are under stress and you have a task you've never solved before. Did I mentioned that you don't know the problem domain and your destiny relies upon your solution you'll be judged for? How would you tackle this problem? What would you say If I'd tell you to write a test first? Sounds crazy, eh? You have so many things to do and you don't want to waste your time on testing. Afterwards, maybe... First you have to write... What are you going to write first? Remember, you aren't familiar with problem. Write a test.

Alright, now the problem statement is as follows:
"Compare two CIDR ranges. 
The significant part of IPv4 Address is prefix
255.255.255.255/24 notation means that only the first 24 bits of the IPv4 address make up the network mask. The bits which are not included in the network mask are not important.
There are several ways to write a CIDR range
These representations are equivalent:
255.255.255.255/24
255.255.255.0/24
255.255.255.100/24
Comparing CIDR Ranges
Ranges can be in subset, superset, disjoint or equal to each other. Write a code comparing them." 

- "Fuck, I never knew what this CIDR notation ment. I should have read about it before. Alright, let's google it. Whoooaaa! I'll read through this for half an hour. What do I do? I've already applied TDD in day to day job, I'll give it a try. First things first, I'll write down what I know about the problem. This is going to be my test list."
  • CIDR range has prefix and suffix parts;
  • Ranges with equal prefix and suffix are equal;
  • Only the Prefix of the IPv4 Address is Significant;
  • A CIDR Range Has Multiple Representations;
- "That will do for the moment. Which one of them do I pick first? I should pick the simplest to implement it as fast as possible. The first one is an ideal candidate for Starter Test."

    [TestFixture]
    public class CidrRangeTests
    {
        [Test]
        [TestCase("23.45.67.89/16", 23, 16)]
        public void when_instantiated_parses_string_into_address_prefix_and_suffix(string range, byte expectedMostSignificantByte, byte expectedSuffix)
        {
            //arrange 
            //act
            var sut = new CidrRange(range);

            //assert
            Assert.AreEqual(expectedMostSignificantByte, sut.Prefix[0]);
            Assert.AreEqual(expectedSuffix, sut.Suffix);
        }
    }

- "Now that the test is written I am going to write the CidrRange class itself. Forgive me Kent, but I am about to jump over one step - I won't Fake It, I consider this code to be an Obvious Implementation and 'll write it straight away. I had lot's of choclate and I can keep things in mind and I have very short time".

    public class CidrRange
    {
        //auto-props for the sake of simplicity
        public byte Suffix { get; private set; }
        public byte[] Prefix { get; private set; }

        public CidrRange(string range)
        {
            //assert not null
            var parts = range.Split('/');
            //assert count 2
            var prefixParts = parts[0].Split('.');
            //assert count 4
            Prefix = prefixParts.Select(byte.Parse).ToArray();
            Suffix = byte.Parse(parts[1]);
        }
     }

- "Green bar, hooray. Time spent? 10 minutes. OK, whats next? The next simplest test to implement - the shortest way to the green bar, is the second one: Ranges with equal prefix and suffix are equal".


        [Test]
        [TestCase("23.45.67.89/16")]
        [TestCase("1.2.3.4/24")]
        [TestCase("172.84.26.128/16")]
        [TestCase("197.54.16.128/25")]
        public void when_prefix_and_suffix_are_equal_should_consider_other_to_be_equal(string range)
        {
            //arrange 
            var sut = new CidrRange(range);
            var other = new CidrRange(range);

            //act
            //assert
            Assert.AreEqual(sut, other);
        }

- "And again, I triangulate and replace fake with implementation in one step. Remeber, I had lot's of chocolate."


        protected bool Equals(CidrRange other)
        {
            return Suffix == other.Suffix && Prefix.SequenceEquals(other.Prefix);
        }

- "OK, running the tests gives me a green bar. Yeah baby. What do I have to complete?"
  • CIDR range has prefix and suffix parts;
  • Ranges with equal prefix and suffix are equal;
  • Only the Prefix of the IPv4 Address is Significant;
  • A CIDR Range Has Multiple Representations;
- "OK, now stop and think. The last one seems to broaden the equality equation for CIDR range. If I put it another way it may sound like the following".
  • when CIDR ranges with exact suffixes differ in insignificant bits, they are considered equal;
- "Cool, I feel for the core of the solution. Now I'll think more about the former: Only the Prefix of the IPv4 Address is Significant, - I may rephrase it like the following:
  • when CIDR ranges with exact suffixes differ in significant bits, they aren't considered equal;
- "OK, so now my test list looks like this":
  • CIDR range has prefix and suffix parts;
  • Ranges with equal prefix and suffix are equal;
  • when CIDR ranges with exact suffixes differ in insignificant bits, they are considered equal;
  • when CIDR ranges with exact suffixes differ in significant bits, they aren't considered equal;
- "Let's code!"

        [Test]
        [TestCase("197.54.16.128/25", "198.54.16.128/25")]
        public void when_significant_prefix_bits_differ_should_not_consider_other_to_be_equal(string leftRange, string rightRange)
        {
            //arrange 
            var sut = new CidrRange(leftRange);
            var other = new CidrRange(rightRange);

            //act
            //assert
            Assert.AreNotEqual(sut, other);
        }

- "Red bar. OK, obviously in order to implement desired behavior I can no longer compare bytes, I have to drill down to bits. What facilities does BCL provides for this? I remember BitConverter and also there is a BitArray class. A-ha! This is what I am going to use, 'cause I don't really want to ressurect my middle age address arithmetic skills. The algorythm is simple: Given the CIDR range 255.255.255.255/24 we know that only the first 24 bits of the IPv4 address make up the network mask, - it means I have to take a prefix, turn it into a bit array, take a suffix, turn it into another bit array starting from the most significant bit and perform a bitwise AND operation in order to get a network mask. After I got both masks, I have to perform an operation, that will tell me if there any difference between them. Well, luckily I had lot's of choclate, and it's obvious that I need to perform a bitwise XOR".


        protected bool Equals(CidrRange other)
        {
            return Suffix == other.Suffix && PrefixesEquals(other);
        }

        private bool PrefixesEquals(CidrRange other)
        {
            var leftMask = new BitArray(Prefix);
            var leftSuffix = new BitArray(32, false);
            for (int i = 31; i >= Suffix; i--)
            {
                leftSuffix[i] = true;
            }
            leftMask = leftSuffix.And(leftMask);

            var rightMask = new BitArray(other.Prefix);
            var rightSuffix = new BitArray(32, false);
            for (int i = 31; i >= other.Suffix; i--)
            {
                rightSuffix[i] = true;
            }
            rightMask = rightSuffix.And(rightMask);

            var result = rightMask.Xor(leftMask);
            return result.Cast().ToArray().All(x => !x);
        }

- "Hmmm, red bar. Something is wrong here."
After five minutes in debugger it appeared that I was creating bit array for mask based on prefix with bytes in reversed order.


    public class CidrRange
    {
        //auto-props for the sake of simplicity
        public byte Suffix { get; private set; }
        public byte[] Prefix { get; private set; }

        public CidrRange(string range)
        {
            //assert not null
            var parts = range.Split('/');
            //assert count 2
            var prefixParts = parts[0].Split('.').Reverse();
            //assert count 4
            Prefix = prefixParts.Select(byte.Parse).ToArray();
            Suffix = byte.Parse(parts[1]);
        }

- "Arghhh, red bar. I'm toast. Come on, keep it on, I've almost found the solution and I've got tests that will prove it. The broken test is simple - Starter test expects to see bytes in reversed order, I'll fix it in a twinkle".


        [Test]
        [TestCase("23.45.67.89/16", 23, 16)]
        public void when_instantiated_parses_string_into_address_prefix_and_suffix(string range, byte expectedMostSignificantByte, byte expectedSuffix)
        {
            //arrange 
            //act
            var sut = new CidrRange(range);

            //assert
            Assert.AreEqual(expectedMostSignificantByte, sut.Prefix[3]);
            Assert.AreEqual(expectedSuffix, sut.Suffix);
        }

- "Finally! Green bar. It took me some time to get here, if I wasn't low on time I'd better be going teeny weeny steps of Fake and Triangulate. OK, now the test list looks like the following".
  • CIDR range has prefix and suffix parts;
  • Ranges with equal prefix and suffix are equal;
  • when CIDR ranges with exact suffixes differ in insignificant bits, they are considered equal;
  • when CIDR ranges with exact suffixes differ in significant bits, they aren't considered equal;
- "Let's code."


        [Test]
        [TestCase("23.45.67.89/16", "23.45.68.00/16")]
        public void when_insignificant_prefix_bits_differ_should_consider_other_to_be_equal(string leftRange, string rightRange)
        {
            //arrange 
            var sut = new CidrRange(leftRange);
            var other = new CidrRange(rightRange);

            //act
            //assert
            Assert.AreEqual(sut, other);
        }

- "red bar, eh? I was supposed to get a green bar!"

After another five minutes in debugger it appeared that I've filled the suffix mask with wrong number of bits and took into account the least significant bits comparing network masks. Fix follows.


        private bool PrefixesEquals(CidrRange other)
        {
            var leftMask = new BitArray(Prefix);
            var leftSuffix = new BitArray(32, false);
            for (int i = 31; i >= 32 - Suffix; i--)
            {
                leftSuffix[i] = true;
            }
            leftMask = leftSuffix.And(leftMask);

            var rightMask = new BitArray(other.Prefix);
            var rightSuffix = new BitArray(32, false);
            for (int i = 31; i >= 32 - other.Suffix; i--)
            {
                rightSuffix[i] = true;
            }
            rightMask = rightSuffix.And(rightMask);

            var result = rightMask.Xor(leftMask);
            return result.Cast().ToArray().Skip(32 - Math.Min(Suffix, other.Suffix)).All(x => !x);
        }

- "A wonderful, beautiful green bar! OK, what do I have?"
  • CIDR range has prefix and suffix parts;
  • Ranges with equal prefix and suffix are equal;
  • when CIDR ranges with exact suffixes differ in insignificant bits, they are considered equal;
  • when CIDR ranges with exact suffixes differ in significant bits, they aren't considered equal;
- "Well, this is not the end, but this was the most interesting part. Following is what is left".
  • CIDR range has prefix and suffix parts;
  • Ranges with equal prefix and suffix are equal;
  • when CIDR ranges with exact suffixes differ in insignificant bits, they are considered equal;
  • when CIDR ranges with exact suffixes differ in significant bits, they aren't considered equal;
  • CIDR range is a subset of other range if prefixes are equal and the suffix is greater;
  • CIDR range is a superset of other range if prefixes are equal and the suffix is less;
  • CIDR range is a disjoint of other range if prefixes differ;
- "I'll take them one by one."

        [Test]
        [TestCase("1.2.3.4/24", "1.2.3.4/16")]
        public void when_prefixes_are_equal_and_left_suffix_is_greater_should_consider_left_to_be_a_subset(string leftRange, string rightRange)
        {
            //arrange 
            var sut = new CidrRange(leftRange);
            var right = new CidrRange(rightRange);

            //act
            var actual = sut.CompareTo(right);
            var expected = sut.Prefix.SequenceEqual(right.Prefix) && sut.Suffix > right.Suffix
                ? RangeIntersectionResult.Subset
                : RangeIntersectionResult.Disjoint;

            //assert
            Assert.AreEqual(expected, actual);
        }

- "Obviously there are more then two results of ranges comparison. There are four of them as follows".


    public enum RangeIntersectionResult
    {
        Equals,
        Subset,
        Superset,
        Disjoint
    }

- "And to compare my range objects I will need another method."


        public RangeIntersectionResult CompareTo(CidrRange other)
        {
            if (Equals(other))
                return RangeIntersectionResult.Equals;

            if (PrefixesEquals(other) && Suffix > other.Suffix)
                return RangeIntersectionResult.Subset;

            return RangeIntersectionResult.Disjoint;
        }

- "Green bar! Hooray."
  • CIDR range has prefix and suffix parts;
  • Ranges with equal prefix and suffix are equal;
  • when CIDR ranges with exact suffixes differ in insignificant bits, they are considered equal;
  • when CIDR ranges with exact suffixes differ in significant bits, they aren't considered equal;
  • CIDR range is a subset of other range if prefixes are equal and the suffix is greater;
  • CIDR range is a superset of other range if prefixes are equal and the suffix is less;
  • CIDR range is a disjoint of other range if prefixes differ;
        [Test]
        [TestCase("172.84.26.128/16", "172.84.26.255/24")]
        public void when_prefixes_are_equal_and_left_suffix_is_less_should_consider_left_to_be_a_superset(string leftRange, string rightRange)
        {
            //arrange 
            var sut = new CidrRange(leftRange);
            var right = new CidrRange(rightRange);

            //act
            var actual = sut.CompareTo(right);
            var expected = sut.Prefix.Skip(1).SequenceEqual(right.Prefix.Skip(1)) && sut.Suffix < right.Suffix
                ? RangeIntersectionResult.Superset
                : RangeIntersectionResult.Disjoint;

            //assert
            Assert.AreEqual(expected, actual);
        }
        public RangeIntersectionResult CompareTo(CidrRange other)
        {
            if (Equals(other))
                return RangeIntersectionResult.Equals;

            if (PrefixesEquals(other) && Suffix > other.Suffix)
                return RangeIntersectionResult.Subset;
            if (PrefixesEquals(other) && Suffix < other.Suffix)
                return RangeIntersectionResult.Superset;

            return RangeIntersectionResult.Disjoint;
        }
  • CIDR range has prefix and suffix parts;
  • Ranges with equal prefix and suffix are equal;
  • when CIDR ranges with exact suffixes differ in insignificant bits, they are considered equal;
  • when CIDR ranges with exact suffixes differ in significant bits, they aren't considered equal;
  • CIDR range is a subset of other range if prefixes are equal and the suffix is greater;
  • CIDR range is a superset of other range if prefixes are equal and the suffix is less;
  • CIDR range is a disjoint of other range if prefixes differ;
        [Test]
        [TestCase("197.54.16.128/25", "197.54.16.127/25")]
        [TestCase("205.00.00.1/32", "205.00.00.00/32")]
        public void when_prefixes_are_different_should_consider_left_to_be_a_disjoint(string leftRange, string rightRange)
        {
            //arrange 
            var sut = new CidrRange(leftRange);
            var right = new CidrRange(rightRange);

            //act
            var actual = sut.CompareTo(right);
            var expected = !sut.Prefix.SequenceEqual(right.Prefix)//this is not exactly correct specification, but it's OK for the sake of simplicity
                ? RangeIntersectionResult.Disjoint
                : RangeIntersectionResult.Subset;

            //assert
            Assert.AreEqual(expected, actual);
        }
  • CIDR range has prefix and suffix parts;
  • Ranges with equal prefix and suffix are equal;
  • when CIDR ranges with exact suffixes differ in insignificant bits, they are considered equal;
  • when CIDR ranges with exact suffixes differ in significant bits, they aren't considered equal;
  • CIDR range is a subset of other range if prefixes are equal and the suffix is greater;
  • CIDR range is a superset of other range if prefixes are equal and the suffix is less;
  • CIDR range is a disjoint of other range if prefixes differ;
- "Time? Wow, I still got some time left. I've manged to complete in under two hours"

This is a real life story disproving the myth of TDD nonproductiveness. Stay tuned as I'll continue my adventures in TDD.

Next time I will refactor this code in a quiet and peacful manner.

P.S. You can find code here.

воскресенье, 14 июля 2013 г.

The Passionate Programmer book findings.

Here I would like to promote to everyone this excelent book.
The Passionate Programmer book tells how not to fall in despair doing your regular programmer's work and work out remarkable software developer career. All ideas presented in form of blog posts related to various areas of selfdevelopment.
This book is full of obvious things, actually. But these are the things you don't always remember about, seeing no forest for the trees.
Thus, I've decided to write'em down, mostly to memorize them better. You may also find these thoughts useful, but I would strongly suggest to come down with your money and buy this book.

It starts with a chapter named "Find your market". Following is a list of recipes how to choose a right market.
  1. Leed or bleed? Both ends of the technology adoption curve might prove to be lucrative.
  2. Supply and Demand. You can’t compete on price. Exploit market imbalances.
  3. Coding don't cut it anymore. Just like technologies that become hot, business domains can be selected in the same way.
  4. Be the worst. This one is my favorite. In short you should always try to find people that are better then you and work with them. The people around you affect your own performance. Choose your crowd wisely.
  5. Invest in your intelligence. Love to learn new things.
  6. Don't listen to your parents. Take calculated riskswith your career.Don’t let fear consume you.
  7. Be a Generalist. Generalists are rare...and, therefore, precious. Your skills should transcend technology platforms.
  8. Be a Specialist. Specializing in something doesn't means not knowing about other things.
  9. Don't put all your eggs in someone else's basket. Though it’s possible to build a business that exists as a parasite of another, as an individual it’s an incredibly risky thing to do. Vendor-centric views are typically myopic.

понедельник, 25 марта 2013 г.

[Ruby frustrations] RubyMine rejects do debug your code?

In short - make sure, that you've removed gem "debugger" from your Gemfile, and have only one ruby-debug-(ide|base19x) version. Then follow these instructions.

Good luck.

среда, 3 октября 2012 г.

[100efforts] What Productivity Factor is and how should one use it?

This is a post on how to use our requirements management and efforts estimation tool and more precisely it is a discussion on what Productivity Factor is. It gives you a simple method of finding reasonable values to start with, in order to receive results that align with real life from the very beginning.

Productivity factor (PF) serves the purpose of translating Use Case points into man-hours. PF is a ratio of number of man-hours per use case point.

Needless to say that PF will be different for various teams and for various phases of a project. The more experienced and well managed team is the more effortless work is.

There are a number of empiric suggestions for choosing a PF value.

First of all, as suggested by industry experts, PF should be between 15 and 30.

Several studies has established a typical PF value of 20, that is why it is a default value for every release you create in 100effors.com

Use this approach to adjust PF for a specific project or phase of a project:
  1. Count the number of environmental factor ratings of E1-E6 that are below 3 and the number of factor ratings of E7-E8 that are above 3.
  2. If the total is 2 or less, then use 20 man-hours per UCP.
  3. If the total is 3 or 4, use 28 man-hours per UCP.
  4. If the total is 5 or more then consider restructuring the project team so that the numbers fall at least below 5. A value of 5 indicates that this project is at significant risk of failure with this team.

And for sure the most reliable method is your past experienceCompare estimated and actual efforts of completed projects/phases (100efforts.com   provides an easy tool for this) and perform adjustments of Productivity Factor for following releases accordingly.

How to set a Productivity Factor in 100efforts.com:
  1. Open your project of interest
  2. Be sure that you have already created at least one release for the project
  3. Go to “Efforts” tab
  4. Click on release name under “Use case points estimation” section and you’ll be landed on the “Summary” page
  5. Click “Productivity factor (PF)” link to enter your value.

References:



понедельник, 16 июля 2012 г.

Status or state after all?



Which of them do you use and when?

This is what I've decided for myself:
"State" - is a set of attributes that define object's behavior and have a state machine of transitions. 
"Status" - is an attribute that holds a single character value.

Other options, suggested by my colleagues:
  • use unthinkingly;
  • use only one of them. always;
That's it, good luck!

четверг, 23 февраля 2012 г.

A business to IT people collaboration tool announcement.

Something tells me that people occasionally landing this page are not so far from software development as my grandma is. 


Thus, I suspect, some of you guys out there might find my new endeavour useful.
We've developed a tool to help collaborate on requirements and usecases, trace them and estimate project efforts. 
A tool I desperately needed at my fulltime job before. 


Starting is simple, either someone adds you as a collaborator of his project and you get an email with a signup link, or you visit our awesome site and signup for our services yourself.




Nothing could have been easier. You will receive a welcome email and can now start working. Anyone can create a project and become its self perpetuating Creator. 


After you've created a project, you are welcome to invite more people to collaborate. When adding people to your project you have to assign them roles. There are three roles except for your exclusive one, namely: Project Owner - usually a business person in charge for requirements; Contributor - any one allowed to contribute to project's items, a Project Manager or Business Analyst; Viewer - a person, whose sole need is to read and observe activities boiling in a project.


So you've created a project and invited your colleagues to collaborate with you. Then you start by creating requirements, functional and nonfunctional. You define a release, you declare it to be mandatory and, say if you know what Kano is, you declare it as a Must have.


You categorize your requirement with tags. Though we provide a powerful set of tools to search through requirements, to sort and filter by literally every attribute, it is always useful to have a short sticker on an item to filter fast.  Filter by releases is in place, of course, for you to laser focus on what's vital for you now.


After you've created a requirement, your colleague takes up and traces it with a number of use cases that define works to be done to achieve required function. He also defines how complex a use case is and who is the actor to participate in it.




And now it comes to the most interesting moment, my favorite - discussion. You've required something no one knows how to accomplish. What can they do? Comment on your requirement or usecase!


Ok, everything is fine. You've managed to figure things out during your long discussion, and now you are ready to estimate how long it will take to develop a project you require.


Wow, it turns out that it is a rather simple task to find an answer.
The last thing to do is specify some details required to tune estimation calculation and you're through with it.




So it goes


I want to remind you that we are still in early stage, and there are plenty of things we have to improve. We need your feedback in order to make our service the bestest =).


Good luck!

понедельник, 16 января 2012 г.

How do you tell a good code?

Agreed with Ayende:
"... most good code bases are actually fairly boring. That is pretty much the definition of a good codebase..."


Source.


Good luck!