www.codinghillbilly.com   kyle.baley.org  Subscribe / Contact
 
 
 
 
LATEST POSTS
Monday, December 28, 2009

Before recently taking on my current two contracts, I flirted briefly with a position as an employee. In the end, I decided not to take the job on the grounds that they didn’t want to hire me and it made me feel better about myself to assume that everyone there is just a bunch of big jerks and I wouldn’t have liked it anyway. As a hint for you job seekers out there: when they fly you in for a three-hour technical interview, you really should bone up on your basics.

What made this a blog-worthy journey was the thought process I went through while I was still a potential candidate. After six years as a consultant, was I ready for employee-hood?

It seems like a no-brainer on the surface. As a consultant, I enjoy considerable freedom, especially with my free time which, as I age, becomes more and more important to me. Prior to my current contract, I hadn’t worked full-time in a year and a half. Mind you, that implies I’m working full-time on my current ones but between new babies, sick older children, spousal holidays, and Xmas, that’s not accurate either. But again, the fact that I have that freedom is why I love consulting.

And because all my contracts are remote these days, I’m usually even flexible with when I work. This depends on the contract but for many of them, as long as I’m available for meetings, the rest of the work can be done whenever I want. Furthermore, I can take time off for conferences or vacation almost whenever I want. I say almost to account for times when the client really needs me but alas, I haven’t been quite that indispensable yet.

“So,” sez you, “why give up all that and more to become an employee working 9-5 on a lower salary with 2 weeks vacation?”

“Don’t interrupt!” sez I, “I’m getting to that.”

There’s a certain fallacy to consulting that we don’t like to talk about. It’s the Hear No Evil syndrome. If we don’t talk about it, it won’t happen to us. It’s related to the “I decide when I want to take a contract” argument. In my experience, this argument is true only about half the time. Because the fact is, when a contract ends, there’s often some level of panic involved. No matter how much money you have squirrelled away to allow for this eventuality, there is always a sense of “what if I can’t find a contract when I’m ready?” Depending on how much panic you feel, you might just end up taking a contract you don’t like. This is especially true these days when the money isn’t flowing like water so much as it is oozing like pus.

Naysayers will argue that employee-ship is no more secure than consultancy. Those people are liars and, very likely, smelly. Employees can be let go, yes, but in my experience, they aren’t the first to go. At my last contract, when the hammer fell, the contractors felt the brunt, not the employees. (Well, at least not directly. The rumour mill suggests that there is a “let’s see how many quit after we introduce THIS policy” program going on there these days.) The contract before that, there was also a mass layoff. I believe a few employees were included in that but again, the rumour mill claims it was pruning talent just as much as cost.

The upshot of this is that in most cases, contractors will get booted before employees. Maybe it’s just a brief respite since laying off all your contractors is rarely a sign of growth but it’s a respite nonetheless. Anecdotal evidence aside, contracts are generally shorter than employee terms and in the end, it’s just plain stressful looking for them. Especially ones that allow you to work remotely 100% of the time. So the prospect of being relatively secure (judging from the effort put into hiring process, the company was one that put a lot of value in its talent) in a position that promised interesting work and still allowed me to work remotely did hold some appeal. Maybe it’s a false sense of security but there’s a reason we still don’t know how placebos work. Perception is a powerful thing.

That said, I’d by lying if I said I didn’t feel a sense of relief when they said they weren’t interested. As much as I freak out when work isn’t handed to me, I’ve been eerily lucky in my consulting career. Even when lengthy involuntary breaks do come (the longest to-date being four months), it ends up leading to more interesting work in the long run because of what I work on and blog about in the interim. As a case in point, one of the contracts I’ve grabbed now is a direct result of my involvement with Sharp Architecture, which, in turn, followed from my delving into MVC two years ago when I was “between contracts”.

I’ve been careful not to explicitly say “being a consultant is better” because I’m almost positive the roundabout way I’ve arrived at the current point in my career is atypical. But here I am and all I can say is that after going through the reflective exercise, I have to conclude that luck was once again on my side when the employee-ship opportunity passed me by. Maybe I’m on borrowed time because I do know a couple of excellent consultants who are actively searching for work (ping me if interested) and in markets that have traditionally been very lucrative, both with the type of work and in the compensation. But until then, it’s time to stop yakking and get back to billable work.

Kyle the Unemployee

Friday, December 18, 2009

Gonna delve away from CodeBetter tradition into more of the realm of our sister site, Devlicious. I.e. This post will be about code, not about thinking about writing code. Having gone through this monstrous exercise though, I gotta say, Ivory Tower Architect is looking like a mighty fine role these days.

I’m working on a search interface that uses NHibernate to search for people using various criteria, such as Languages Spoken (generally a zero to many relation), Special Skillz, and Preferred Side Dish for Various Roadkill. It uses HBM files and a couple of the collections on the Person object are mapped as composite-elements, like so:

    <class name="Person" table="People">
    ...
        <bag name="SpecialSkills" table="Mad_Skills">
            <key column="PersonFK" />
            <many-to-many class="Skill" column="SkillFK" />
        </bag>
    ...
        <bag name="PersonalHygieneMilestonesReached" table="Personal_Hygiene">
            <key column="PersonFK" />
            <composite-element class="PersonalHygiene">
                <many-to-one name="HygieneAction" column="HygieneFK" />
                <property name="LastPerformed" column="Date_Last_Performed"/>
            </composite-element>
        </bag>
        
    ...
    </class>

This is just a representative sample. The Person class has a collection of Skill objects (accessed via a property named SpecialSkills) as well as a collection of PersonalHygiene objects (mapped via the PersonalHygieneMilestonesReached property). The PersonalHygiene object contains a HygieneAction object (which is just an ID and Description) and a DateLastPerformed property.

In the search interface, we'd like to search for people who have, say, bathed. (I'm ignoring the date last performed to avoid muddying the wa--err...clouding the issu---ummm, complicating things.) So the search interface gives us a dropdown list, the user selects “bathed”, and our NHibernate-based data access class goes on its merry way.

public IList<Person> FindByPersonalHygiene( int PersonalHygieneId )
{
    var query = DetachedCriteria.For<Person>( )
        .CreateAlias( "PersonalHygieneMilestonesReached", "hygiene" )
        .Add( Property.ForName( "hygiene.Id" ).Eq( PersonalHygieneId );
    return query.GetExecutableCriteria( Session ).List<Person>( );
}

Similarly, here's how we could find everyone who has a given special skill, say, "Converses at a Third Grade Level.":

public IList<Person> FindBySpecialSkill( int SpecialSkillId )
{
    var query = DetachedCriteria.For<Person>( )
        .CreateAlias( "SpecialSkills", "skill" )
        .Add( Property.ForName( "skill.Id" ).Eq( SpecialSkillId );
    return query.GetExecutableCriteria( Session ).List<Person>( );
}

All well and good and does what we want. It’s slightly different than searching on a property of the Person object in that we are searching for the existing of an item in one of its collections. Hence, the use of CreateAlias.

The next task: Add the ability to apply multiple filters to our list of people. If someone matches ANY of the supplied criteria, they are returned by the search. That is, we want to OR the criteria together.

Let's search for anyone that has bathed OR has the ConversesAtAThirdGradeLevel special skill. Luckily, there is an Expression.Or method that, in theory, should make this easy. Let's take a look at this special case before generalizing some.

public IList<Person> FindByHygieneOrSkill( int PersonalHygieneId, int SpecialSkillId )
{
    var query = DetachedCriteria.For<Person>( )
        .CreateAlias( "PersonalHygieneMilestonesReached", "hygiene" )
        .CreateAlias( "SpecialSkills", "skill" )
	.Add( Expression.Or( 
            Property.ForName( "hygiene.Id" ).Eq( PersonalHygieneId ), Property.ForName( "skill.Id" ).Eq( SpecialSkillId )
        );
    return query.GetExecutableCriteria( Session ).List<Person>( );
}

This is essentially a combination of the two queries. We need to add all the aliases up front because Expression.Or takes two ICriterion objects, not two ICriteria objects. This can be generalized as well by modifying the FindByXXX methods so that they look more like this:

public ICriterion AddAliasAndGetCriteriaForSpecialSkill( DetachedCriteria query, int SpecialSkillId )
{
    query.CreateAlias( "SpecialSkills", "skill" );
    return Property.ForName( "skill.Id" ).Eq( SpecialSkillId );
}

public IList<Person> GetPeopleMatching( SearchCriteria criteria )
{
    var query = DetachedCriteria.For<Person>( );
    var disjunction = new Disjunction( );
    if ( criteria.SpecialSkillId.HasValue )
    {
        var criterion = AddAliasAndGetCriteriaForSpecialSkill( query, criteria.SpecialSkillId.Value );
        disjunction.Add( criterion );
    }
    if ( criteria.PersonalHygieneId.HasValue )
    {
	var criterion = AddAliasAndGetCriteriaForPersonalHygiene( query, criteria.PersonalHygiene.Value );
        disjunction.Add( criterion );
    }
    query.Add( disjunction );
    return query.GetExecutableCriteria( Session ).List<Person>( );
}

Kinda messy with all the if statements and the specialized AddAliasAndGetCriteriaForXXX methods but those can be cleaned up. I switched to using a Disjunction because it allows you to chain a bunch of ICriterion objects together, rather than limiting it to two like Expression.Or does.

The reason I won't show you the cleaned up code because when all is said and done, this won't actually work. If you look at the underlying query being executed, it looks something like this:

SELECT *    -- Not really * but you get my drift
FROM Person p
INNER JOIN Person_SpecialSkills pss ON p.Id = pss.PersonId
INNER JOIN Person_PersonalHygiene pph ON p.Id = pph.PersonId
WHERE pss.SkillId = @p0 OR pph.PersonalHygieneId = @p1

On the surface, this looked correct to me intuitively. It gave me results and those people had the specified skill and/or hygiene habit.

But I noticed there were people missing in the results. Specifically, people that had either no special skills or no personal hygiene habits. Which is when the INNER JOIN assignment from my second year databases class came rushing back to me. Of course, if you use INNER JOIN, you exclude any data where there is no match between the two tables. So my little SQL statement above will include only people who have BOTH a special skill AND personal hygiene and only those that match the given criteria.

Instead, I could use OUTER JOIN. But I didn’t much like how things were starting to meld together with the aliases and the criteria so I decided on a separate approach. In essence, for each search filter, I want to run a query. Then I want to return the list of people from all those queries. I could do that programmatically, I suppose. I.e. Execute the queries, then use List.Intersect to pull them all together.

But that ain’t how the hillbilly rolls. Instead I went with subqueries. The underlying pseudo-SQL:

SELECT *
FROM Person
WHERE Id IN ( SELECT PersonId FROM Person_SpecialSkills WHERE SkillId = @p0 )
OR Id IN ( SELECT PersonId FROM Person_PersonalHygiene WHERE PersonalHygieneId = @p1 )

To achieve this, I need slight modification to my original method

public DetachedCriteria FindByPersonalHygiene( int PersonalHygieneId )
{
    var query = DetachedCriteria.For<Person>( )
        .SetProjection( Projections.Property( "Id" ) )
        .CreateAlias( "PersonalHygieneMilestonesReached", "hygiene" )
        .Add( Property.ForName( "hygiene.Id" ).Eq( PersonalHygieneId );
    return query;
}

The changes: return the actual query and specify a projection, which is fancy-Hibernate-speak for "what fields to I want this query to return?".

From here, I created the main search method:

In this case, finders is a dictionary of Func<int, DetachedCriteria> objects keyed on a SearchType enum. It's initialized elsewhere. I use it as a sort of pseudo-strategy-type thing and if you claim that it's not *really* a strategy pattern, I won't disagree because I don't care. I just know it's cleaner than using a switch statement.
public IList<Person> FindBy( IList<SearchExpression> expressions )
{
    var query = Session.CreateCriteria( typeof (Person) );
    var disjunction = new Disjunction( );

    foreach ( var expression in expressions )
    {
        var subquery = finders[expression.SearchType]( expression.Id );
        disjunction.Add( Subqueries.PropertyIn( "Id", subquery ) );
    }

    query.Add( disjunction );
    return query.List<Person>( );
}

The main method is pretty clean now. It loops through the list of all the filters the user has selected and adds an appropriate subquery, checking to see if the Person.Id is in the returned query.

Now some of you may cry foul in the name of performance or some other NHibernate feature that makes all this obsolete. To you, I say, software is a journey, not a destination.

Kyle the Tacit

Tuesday, December 15, 2009

Some time ago, I had a small crisis of conscience as I worked on an app for my dad’s company. At the time, I wondered about the long-term viability of using so-called advanced tools like NHibernate, IoC, and MVC. Specifically, if I drop dead (and it’s starting to get a little morbid the number of times this comes up in conversation, especially with my family), will the next guy they get be able to understand it? More generally, how much do I alter my coding practices to make it more palatable to someone who might be more “learning averse”.

Cut to a year and a half later and the following has happened:

  • NHibernate has become much more well-known and easier to work with. Plus the available documentation has increased immensely.
  • MVC is no longer Scott Guthrie’s side project and the only issue now is that the version I used was pre-release.
  • The company got sold and they are phasing the application out.
    Full disclosure, there is one HUGE reason I was able to ramp up quickly on the Sharp project. Rather than send me the code/database or point me to an SVN URL, he shipped me a virtual machine pre-configured. All the projects, dev tools, and configuration was done so there was none o’ this “download the latest ELMAH and stick it in this folder” and “create this IIS app” and such. Just “here’s your VM. Go install VPC”. Granted, I did spend some time converting it to VMWare because the latter is more conducive to the Kinesis keyboard I’m using (which doesn’t have a Right-Alt key). But even that was a painless process.
  • Most notably, I didn’t die

All of these add up to a reaffirmation that I did the right thing by sticking to what I do best. I got the application done that much quicker and all the tweaks I made to it over the months (which, admittedly, wasn’t that much) were made much faster than if I had, say, hand-rolled a DAL with stored procedures. At least, I perceive that to be true. And in this industry, of course, perceived performance is better than actual performance.

That’s not actually the crux of this post but darned if it didn’t make me feel good writin’ it out.

The Hillbilly has picked up a couple of new contracts and is, once again, employed. Whether or not it’s gainful employment has yet to be determined but for the last couple of months, it has been a mellower experience than my previous contract.

Part of the reason for the calmer work atmosphere is that one of the contracts is based on Sharp Architecture. In fact, it is with none other than Sharp’s creator and benefactor, Billy McCafferty, who is as gracious a host as one would expect from someone who documents his OSS as well as he does.

The other contract is similar in that it’s ASP.NET development but it’s web forms, not MVC. It contains a single project and uses Linq to SQL data models for the data access. It’s not spaghetti code, mind you. There’s a healthy dose of copy and paste and the code-behind-thingy file from the .dbml file is constantly being re-written and/or deleted at regular intervals but for the most part, it’s relatively clean and easy to work with once you get the hang of it.

I started each contract at about the same time and here’s where the title comes in. It took me less time to get up to speed on the Sharp project as it did the other one. Not because I know MVC more than web forms but because I didn’t have to spend a lot of time figuring out the project’s conventions. How does data access work? Where does the business logic go? Where do I store things on the file system? Where do I get all the dependencies?

So I got to thinkin’ about my unfounded trepidation for the previous contract. Yes, I could have dumbed it down so the next person was able to understand it. But what if the next person already had the skill set and/or the wherewithal to recognize the patterns and how to learn what he or she needed to know? I.e. what if the next person was <shudder> BETTER THAN ME?!? (And at this point, being better than me entails only that he or she recognizes that I should have said “better than I”). Would I not be doing that person a favour by allowing him or her to grow as a developer and possibly to get the work done quicker?

This is all conjecture and reeks of arguing the happy scenario to prove my point. And I’ll be damned if I’m going to reek based solely on invalid arguments. So I’ll alter my point slightly. Maybe I got lucky in that the application had a short shelf-life and we never got the chance to see who the next person was. Perhaps I acted recklessly by increasing potential long-term costs of the project. Or maybe I did the right thing by not dumbing down the software for some possible future event. There will always be anecdotal evidence to prove either side, methinks. Case in point: at my last contract, where learning new techniques was just below washing one’s hair with a cheese grater on the comfort scale, the rumour mill says all software development has ceased in favour of off-the-shelf solutions. Makes me wonder how much was spent ripping out code that was deemed “too complicated”.

So for the time being, I believe I’ve justified myself into doing what I want and how I want it, when given the choice.

Kyle the Justified

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Copyright © 2010 Kyle Baley. All rights reserved.
 
CATEGORIES
.NET General (18) alt.net (4) altnetconf (9) ASP.NET AJAX (40) ASP.NET MVC (29) Bahamas (1) Bahanet (9) BDD (1) Brownfield (18) Career (9) Castle (1) Code coverage (1) Coding Style (6) Communication (1) Community (18) Conscientious Coding (34) Continuous Integration (11) dasBlog (12) Development (16) DevTeach (4) Domain (2) Environment (4) Estimating (1) Featured (14) Flamingo (10) Games (1) Google App Engine (2) GWT (5) Hardware (6) Java (1) Javascript (7) Linq (2) Livelink (6) Lucene.NET (2) MbUnit (1) Metrics (1) Miscellaneous (24) Mocking (4) NAnt (4) NHibernate (12) NInject (1) Office (3) Office Development (6) Open Rasta (1) Patterns (5) Presenting (13) Professional Development (15) Refactoring (10) ReSharper (11) REST (2) S#arp Architecture (5) Security (3) Software (11) Sundry (18) TDD (19) Tools (21) User Interface (5) Utilities (8) Visual Studio (8) VSTO (1) Web development (12) Windows (3) Working Remotely (16) Workplace (3) Writing (4)
 
LATEST POSTS
 
POPULAR POSTS
 
 
ARCHIVE