Monday, July 18, 2011

The exponential learning curve from studying off-hours

You work a full hard day, so why bother "studying" off-hours? Because, it has an exponential reward. The trick is to differentiate between daily "grunt" work that just takes time without improving you as a developer, vs. "learning" work, such as experimenting with new technology or patterns or tools.

I've seen endless resumes where the candidate says "I have 5 years experience in technology X", but it's really 1 year of experience repeated 5 times. They've sadly spent their career doing repetitious work, and have nothing new to show for it.

Here's a simplistic case: say you spend 9 hours a day ("45" is the new "40") at work, but 8 hours of that is grunt work – data access plumbing, fix a logic bug, attend yet another meeting where you just sit through it, fill out a timesheet – and you've snuck away only 1 hour to research some new data-performance prototype, that means about 90% of you day is grunt work, and about 10% is advancing your career. If you did 1 hour self-study at night, it's not that you go from 9 hours to 10 hours, but rather from 1 hour to 2 hours, i.e. that extra hour is "cool" stuff for self-study, so it gives your "self-study" a  100% return.

Because I love Excel, here's a chart. "Work hours" is split between "Grunt hours" and "self-study hours". Hence an extra hour or two at night could double your learning curve.
I realize this is very black and white, and live is grey (for example, our jobs aren't neatly divided into "grunt " and "self-study", and 1 hour of self-study may be split into a dozen 5-minute Google queries throughout the day), but the general idea still holds.


Work hours
Grunt hours
Self-study hours
Self-study
Percent
Overtime
Self-study
Self-study
increase
9
9
0
0%
1
Infinite
9
8
1
11%
1
100%
9
8
1
11%
2
200%
9
7
2
22%
1
50%
9
7
2
22%
2
100%


Related to this is the Upward Spiral – that the extra hour of overtime helps you learn a technique that makes your day job much faster. For example, say you spend 4 hours a day writing plumbing code to filter C# objects, but then you study LINQ during the evenings and now can do a 4-hour job in 30 minutes. That theoretically gives you a "surplus" of 3.5 hours. Of course that time gets snatched up by other things, but in general it's reasonable to reinvest part of that time in yet further self-study, i.e.  compound interest for your development career.

Friday, July 15, 2011

Presentation: An Introduction to Practical Unit Testing

I had the opportunity to speak at the LCNUG yesterday about one of my favorite topics - unit testing.

For convenience, here are some of the links from the PowerPoint:

Database "Unit Testing"
IOC

Tuesday, July 12, 2011

Upcoming talk: July 14 - An introduction to practical unit testing

[UPDATE] - slides at: http://www.timstall.com/2011/07/presentation-introduction-to-practical.html

I'll be presenting at the Lake County .Net Users Group this Thursday, July 14, on An Introduction to Practical Unit Testing.
Unit testing is one of those buzzwords that every developer hears about, but relatively few projects actually do in a way that adds value. Many developers view unit tests as some "tax" imposed by management. This session will show how to start using unit tests to add immediate value, as well as dispel several common myths and abuses of unit testing. It will explain how unit tests fit with other types of automated tests (integration, functional, UI, performance), as well as how unit tests are but one technique in a developers' tool belt to craft better code.
This is intended as a basic 101-level session.

Tuesday, June 28, 2011

Query files in the TFS VersionControl database

TFS provides an API that C# could programmatically query source control. However, even with Linq, that may become tedious coding. TFS also provides a TfsVersionControl database that you can query directly with SQL. This has power.
Why use the undocumented TfsVersionControl database when you're "encouraged" to use TfsWareHouse?
  1. The Transaction databases (TfsBuild, TfsVersionControl, TfsIntegration) are realtime, so you don't need to wait 30 minutes - 2 hours for it to refresh.
  2. Not all the info is migrated to the TfsWareHouse (or at least, I can't find it in any documentation). For example, the warehouse has a File table, but it doesn't contain all versioned files (such as binaries, images, etc...)
  3. The TFS warehouse may be corrupted (the process to sync it may be down)
Here's a simple (TFS 2008) query to get you started. It contains version, the full path, file name, and the CreateDate (when it was checked in). It's based on versioned items, so you can query history (you may also get duplicated, so you'd need to query that).
select
V.VersionFrom, V.FullPath, L.CreationDate,
Replace(V.ChildItem, '\', '') as [FileName],
V.*, L.*
from tbl_Version V (nolock)
inner join tbl_File L (nolock) on V.FileId = L.FileId
where V.ParentPath = '$\MyTeamProject\Folder\SubFolder\'
order by V.VersionFrom desc
Note that TFS by default stores paths in a different format, so you may need to convert:
·         '/' becomes \
·          '_' becomes >
·         '-' becomes " (double quote)

Monday, June 27, 2011

Linq: Creating new objects from selects and joins

I like Linq more every time I use is. I've posted about XLinq and using linq to sort and filter lists. You can also use Linq to join two objects and select properties from each to create a new collection of objects (somewhat like SQL), run ForEach clauses, and do simple functions like Distinct, Sum, and Count.
Here's a code sample (I prefer to do minimalist samples with a unit test syntax for easy demos):

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace LinqDemo
{
      /// <summary>
      /// Summary description for UnitTest1
      /// </summary>
      [TestClass]
      public class UnitTest1
      {
            private Employee[] GetEmployees()
            {
                  return new Employee[]
                  {
                    new Employee(){ FirstName = "Homer", LastName ="Simpson", FavoriteNumber=7, DeptId=1},
                    new Employee(){ FirstName = "Marge", LastName ="Simpson", FavoriteNumber=18, DeptId=0},
                    new Employee(){ FirstName = "Bart", LastName ="Simpson", FavoriteNumber=99, DeptId=0},
                    new Employee(){ FirstName = "Monty", LastName ="Burns", FavoriteNumber=23, DeptId=9},
                    new Employee(){ FirstName = "Ned", LastName ="Flanders", FavoriteNumber=5, DeptId=0}
                  };
            }

            private Department[] GetDepartments()
            {
                  return new Department[]
                  {
                        new Department(){ DeptId=1, DeptName="Safety Operator"},
                        new Department(){ DeptId=2, DeptName="Customer Service"},
                        new Department(){ DeptId=9, DeptName="Executive"},
                  };
            }



            [TestMethod]
            public void SelectProperties()
            {
                  //create data
                  Employee[] emps = GetEmployees();

                  //Use linq to get a distinct list from some property
                  List<string> lastNames = emps
                        .Where(n => n.FavoriteNumber > 10) //Some filter
                        .OrderBy(n => n.LastName)
                        .Select(n => n.LastName) //Select specific fields
                        .Distinct() //Get only distict elements
                        .ToList();

                  Assert.AreEqual("Burns", lastNames[0]);
                  Assert.AreEqual("Simpson", lastNames[1]);
            }

            [TestMethod]
            public void JoinAndCreateAnotherObject()
            {
                  //Join "Employee" and Department to create "Worker"
                  Employee[] emps = GetEmployees();
                  Department[] depts = GetDepartments();

                  //This could be useful is emps and depts came from
                  //    different sources, or depts was cached
                  Worker[] workers =
                        (
                              from emp in emps
                              from dept in depts
                              where emp.DeptId == dept.DeptId
                                    && emp.DeptId > 0
                              select new Worker()
                              {
                                    FirstName = emp.FirstName,
                                    DeptName = dept.DeptName
                              }
                        ).ToArray();

                  Assert.AreEqual("Homer", workers[0].FirstName);
                  Assert.AreEqual("Safety Operator", workers[0].DeptName);
            }

            [TestMethod]
            public void ForEach()
            {
                  //Use a single line to update a property
                  List<Employee> emps = GetEmployees().ToList();
                  Assert.AreEqual(7, emps[0].FavoriteNumber);

                  //Double everyone's favorte number
                  //Easier than writing a for-each loop
                  emps.ForEach(n => n.FavoriteNumber = n.FavoriteNumber * 2);

                  Assert.AreEqual(14, emps[0].FavoriteNumber);
            }

            [TestMethod]
            public void Do_Aggregates()
            {
                  //Get the sum of all numbers where the number is already > 10.
                  List<Employee> emps = GetEmployees().ToList();
                  int intSum = emps
                        .Where(n => n.FavoriteNumber > 10)
                        .Sum(n => n.FavoriteNumber);
                  Assert.AreEqual(140, intSum);
            }


      }

      public class Employee
      {
            public int FavoriteNumber { get; set; }
            public int DeptId { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }

            public override string ToString()
            {
                  return string.Format("{0} {1} - {2}", this.FirstName, this.LastName, this.FavoriteNumber);
            }
      }

      public class Department
      {
            public int DeptId { get; set; }
            public string DeptName { get; set; }
      }

      public class Worker
      {
            public string FirstName { get; set; }
            public string DeptName { get; set; }
      }


}