Reverse Technical Interviews
One of the things I've had to do quite often in the last few years is conduct technical interviews.
It's always a challenge as of course, if you want to make sure that a resource is technically competent in specific tasks or role for which you are resourcing, that's pretty easy; just ask questions oriented around those tasks that they will be responsible for. But what if you want to assess an individual's broader technical competency of and experience with a platform?
As a technically oriented guy myself, it's very easy to inject my bias and my core knowledge into the equation and I think that typically makes for a bad interview. There are always many things that I know well and many things that I don't know well for any given platform. So how can I go about measuring a candidate's competency without injecting my own knowledge and experience bias?
As an addendum, for technical interviews, it's sometimes difficult to come up with a good set of questions that are "fair". I don't think it's fair to ask obscure questions for which few folks would know off the top of their head, but would have no problem solving with Google and StackExchange, for example. I also don't like to ask brain-bender type questions as I don't find the outcome of those questions to be generally useful in evaluating technical expertise.
One approach is to ask open-ended design type questions. "Given this platform, how would you design a solution to meet requirement X?" "What are the benefits of approach U versus approach V for modeling this data?" These are okay, but I find that I often get clouded by situational bias. What I mean by that is that I tend to think of problems I've solved in the recent past or problems that I'm working on now. But I know that many of these design issues took me days if not weeks of research, prototyping, experimenting, and discussion to settle upon -- it simply doesn't seem fair to ask a candidate to produce a response on the spot. It's worth something, I guess, if they are able to come up with the same solution (or a better one!), but if they can't, does one hold that against them?
So in thinking about these issues, I think one good approach is to go with a reverse technical interview. What this means is that I ask the candidate to produce a list of technical and design questions for me for the interview. My thought is that this allows me to turn the bias that I would otherwise have into a tool because they will have the same biases. They will tend to ask questions around what they've worked on, what hard problems they've solved, and what experience they have. This seems like a much more dynamic approach and would seem to provide more valuable insight...I think. It's one thing to list a skill or a technology on your resume, but it's another thing to be able to ask deep, challenging, technical questions around it.
As a bonus, being able to come up with and ask good questions is itself a valuable skill.
Pineapple Status Update
My pineapple first showed a fruit in January and I was worried that it would do...something...while I was away. It's been slow going, but here it's still going:
Procedural vs. Structural Code
In working with my development team, one of the things I've been working on is to figure out how to get them to be more object oriented. I've written about this topic before and I continue to evolve my expression of this idea to other developers that I work with.
To me, beyond the simple textbook definition of object oriented programming, at the level of construction, what it really boils down to is expressing logic structurally as opposed to procedurally. That is the very basis of what I've been trying to communicate and trying to understand how to impart that on other developers.
As a basic example, let's consider a user management API that has three operations for add, update, and delete. Suppose I want to send a sequence of actions to this API. We see this type of pattern of interaction all the time. Assume we have a repository class:
public class UserRespitory
{
public void Add(User user)
{
Console.Out.WriteLine("Added!");
}
public void Delete(User user)
{
Console.Out.WriteLine("Deleted!");
}
public void Update(User user)
{
Console.Out.WriteLine("Updated!");
}
}
I've left the implementation of the actions intentionally simple. Now assume that we have an operation and a user class like so:
public class Operation
{
private readonly string _type;
private readonly User _user;
public Operation(string type, User user)
{
_type = type;
_user = user;
}
public string Type
{
get { return _type; }
}
public User User
{
get { return _user; }
}
}
public class User
{
private string _username;
public User(string username)
{
_username = username;
}
}
Consider a use case where we must build an API to support bulk operations. The typical implementation pattern that I will encounter will look more or less like this:
private static void Main(string[] args)
{
List<Operation> inputs = new List<Operation>
{
new Operation("Add", new User("Charles")),
new Operation("Update", new User("Steve")),
new Operation("Delete", new User("John"))
};
UserRespitory repository = new UserRespitory();
foreach (Operation operation in inputs)
{
switch (operation.Type)
{
case "Add":
repository.Add(operation.User);
break;
case "Update":
repository.Update(operation.User);
break;
case "Delete":
repository.Delete(operation.User);
break;
default:
throw new InvalidOperationException();
}
}
}
This is pretty much textbook procedural code and, as a consultant, this is pretty much the code I expect to see (and have seen countless times) on projects. Of course, this is a pretty harmless case; in measuring the cyclomatic complexity and maintainability index of this code using Visual Studio's built-in analysis tools, I get values of 7 and 67 for each respectively.
Cyclomatic complexity is a good tool because it gives us a quantitative metric of code quality that is indifferent to "style" or any qualitative prejudices that individual developers might have -- it's a pretty good starting point for determining where to focus your time in terms of code reviews and refactoring. With that said, a value of 7 is certainly not bad (the NIST guidelines recommend a limit of 10 for methods), however, I think we've all encountered this before: if-else's nested under switch's nested under if-else's nested under for-loops and so on. When I see this pattern, I usually encounter it in the 20-30 range (and I've seen it in the 100+ range, sadly (an extract from a 1078 line method with cyclomatic complexity of 136 in production code -- I couldn't zoom out far enough to capture the whole thing)) for cyclomatic complexity simply because the developer doesn't know when to stop the madness.
Well, let me correct that last statement: when a developer starts down this path and once they've gone deep enough, there is simply no way that they can recover from the madness without structurally refactoring the code; the switch-case traps you into a cycle of cancerous code unless you can find a different way of representing the same logic in a more modular, orthogonal manner. To me, this is really the heart of understanding object oriented programming. How we achieve this -- in this case -- is quite simple, really. For starters, we make the operation abstract:
public abstract class Operation
{
private readonly User _user;
public Operation(User user)
{
_user = user;
}
public User User
{
get { return _user; }
}
public abstract void Execute(UserRepository repository);
}
We also add an abstract method called Execute which every inheriting class must implement -- it's here that we've captured the essence of the switch-case in a much more structural, object-oriented manner. Instead of a switch statement, we build the same logic structurally into the code by using three classes:
public class DeleteOperation : Operation
{
public DeleteOperation(User user) : base(user) {}
public override void Execute(UserRepository repository)
{
repository.Delete();
}
}
public class AddOperation : Operation
{
public AddOperation(User user) : base(user) {}
public override void Execute(UserRepository repository)
{
repository.Add();
}
}
public class UpdateOperation : Operation
{
public UpdateOperation(User user) : base(user) {}
public override void Execute(UserRepository repository)
{
repository.Update();
}
}
The abstract method now gives us a way of specifying a different action for each "case" by creating another class. Now our main code can be updated:
private static void Main(string[] args)
{
// Structural representation of the logic
List<Operation> inputs = new List<Operation>
{
new AddOperation(new User("Charles")),
new UpdateOperation(new User("Steve")),
new DeleteOperation(new User("John"))
};
UserRepository repository = new UserRepository();
inputs.ForEach(i => i.Execute(repository));
}
What is our end result?
The change in the code itself was very minor; we performed a slight-of-hand and simply moved the three logical branches in the switch into three separate classes instead with each class inherently representing one of the switch cases. In doing so, however, the cyclomatic complexity for the main program halved to 3 and the the maintainability index increased 6 points, even with such a barebones change.
The overall maintainability index of the entire solution increased 6 points. Our cyclomatic complexity for the solution did increase, but that's because we've added three new classes which adds to the sum.
There are several other advantages that are easy to extend from this point:
- It's easier to add new operations. Instead of adding another case, we simply add another operation class and inherit from the abstract base class. The logic for that particular operation -- for example, business logic or setup before the operation is invoked -- can be encapsulated in the new class instead of in the case.
- It's easier to add more logic around the operations. Because we've removed the invocation from the switch, we can avoid a giant if with deeply nested code as the logic around the operations change. With a class for each operation, we can add operation specific logic to the class instead. If we need to add common logic, we can use the base class.
- It's easier to read. I think the code is easier to read simply because the level of nesting is now greatly reduced. McConnell writes in Code Completed, 2nd Edition:
"The smaller part of the job of programming is writing a program so that the computer can read it; the larger part is writing it so that other humans can read it." (McConnell, 733)
- It's less prone to user error. By removing the switch and getting away from a string comparison, we've made the solution more resilient to input errors.
Overall, it's a very minor change in the structure of the code (check it out, even the LoC is the same), but even in this most basic of scenarios, it yields clear objective and subjective improvements. While this is a very basic example of a very specific case, the underlying idea here -- structural representation of logic and flow -- is the real essence of object oriented programming. The intentionally simplistic example is meant to (hopefully) help surface this concept, but the general idea of refactoring to structural representations is applicable to many different use cases, scenarios, and coding patterns.
Code samples: SampleProcedural.zip and SampleStructuralCode.zip
Book Review: 17 Equations That Changed the World
What could be more boring than mathematical equations? The majority of folks would be hard pressed to find something to answer that hypothetical query. Myself included
I'll be honest, I'm a math minor and I picked up this book on a whim in a bookstore thinking to myself "Now why would anyone want to write or buy a book on 17 equations?" I flipped through it and immediately knew that I had to consume the rest of this book.
What Stewart is able to do is to take these 17 equations that manifest in everything we do, everything we observe, everything bit of space around us and bring life to them. He presents the opening of each chapter with a concise summary of these equations that helps immensely in revealing the underlying nature of the equations and then goes into the history of the creation (discovery?) of each of these equations and it's been an eye-opening read.
As an example, having majored in computer science, I worked constantly with logarithms and natural logs (there's lumber joke here somewhere) but never once understood the nature of logarithms. How did they come about? Why do they exist? What problem do they address? Just what in the heck is a logarithm? I knew them only in the abstract -- as operations that yielded a result; I knew them as a general pattern but not the nature of the logarithm. The second chapter simply blew me away with the clarity and simplicity with which Stewart was able to pull back the covers on what logarithms actually mean. No one in my years of formal education had bothered to explain it in the same way that Stewart does in this book.
While I cannot say that this book is for everyone, I will say that I find it is surprisingly approachable for most folks who are scientifically or mathematically inclined. Certainly, there are many equations and plenty of mathematics (and it gets especially complex (pun intended
in the later chapters. However, I think this book is still immensely readable and approachable, even for those who have never ventured deep into the vast field of mathematics or have long moved past their days of calculus, linear algebra, and so on. I, for one, will make sure that my daughter reads the chapter on logarithms as it starts to seep into her curriculum one day to make sure she understands the "why" and so that she has an appreciation for all of the history and magic behind that little "log n" button on her calculator.
This book is incredibly well written, well presented such that it is approachable for a large audience, an entertaining read, and highly recommended. If you've read this review to this point, you should probably just go ahead and by this book!
The ESAM 5600 Arrives!
Ever since my trip out to Switzerland, I've been jonesing for an espresso machine. After a lot of research, checking out lots of youtube videos, almost pulling the trigger various times, I ended up purchasing the DeLonghi ESAM5600 SL from JL Hufford. I briefly considered Nespresso machines, but without recycling centers nearby, it seemed like very wasteful and not to mention the cost of the pods would be expensive over the lifetime of the machine. Besides: I like to be able to choose my own beans.
We'll see how this DeLonghi holds up over time. I expect to get a lot of use out of it
Windows Azure “Introductory Special” — WTF?
WTF Microsoft? Seriously?
This is what you call an "introductory special"?
This is fine and all, except in the months of January and February -- in fact, since I've signed up -- I've never used the caching service. Heck, I've barely used Azure! How can I possibly be billed for $46? Yeah, I'm pretty much cancelling my sub after this; can't waste my time and energy every month double checking to see if I'm being ripped-off by Microsoft. And on top of all that, I have to wait 4 hours for a support callback...
Now here is my theory: I am being billed $4/day just for having 128MB of cache allocated, whether I use it or not. But this is beyond fucking stupid. It hurts adoption because it drives developers -- like me -- away from even signing up in the first place if I have to police by billing, even when I'm not using the service. And seriously $46/mo. for having 128MB of web accessible memory available? I get more memory allocated from my $7/mo. hosted shared server. WTF?
I can accept the cost if there was some way to opt out or if there was some notice that I would be billed for being allocated the cache space or if there was some à la carte way to turn it off -- my mistake for not noticing in those cases. But I was not given an option to opt out of cache, I was not notified that I would be billed regardless of actual usage, and as far as I can tell, there is no way to turn off cache.
This is some serious bullshit.
Folders and Nested Folders in ListInstances
This took me about 3 hours to figure out so I figure it was worth sharing.
First, I assumed that it was possible given that the .stp file export of a custom list with folders and nested folders seem to work fine. Online, the feedback seems to be a bit different and my early results seemed to confirm it. However, I persisted and after several (and I mean several) tries, I finally got it working.
So I share with you the magic sauce for those times when you want to seed your deployment with hierarchical data.
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ListInstance Title="Seed Data"
OnQuickLaunch="TRUE"
RootWebOnly="TRUE"
TemplateType="10000"
Url="Lists/SeedData"
Description="Seed data">
<Data>
<Rows>
<Row>
<Field Name="ID">1</Field>
<Field Name="ContentType">Country</Field>
<Field Name="Title">USA</Field>
<Field Name="FSObjType">1</Field>
</Row>
<Row>
<Field Name="ID">2</Field>
<Field Name="ContentType">State</Field>
<Field Name="Title">New Jersey</Field>
<Field Name='FileDirRef'>USA</Field>
<Field Name="FSObjType">1</Field>
</Row>
<Row>
<Field Name="ID">3</Field>
<Field Name="ContentType">County</Field>
<Field Name="Title">Mercer</Field>
<Field Name="FileDirRef">USA/New Jersey</Field>
<Field Name="FSObjType">0</Field>
</Row>
</Rows>
</Data>
</ListInstance>
</Elements>
Yes, it was a pretty painful experience to get this figured out, but it was definitely worth it.
The Pineapple Finally Fruits!
It's finally happened! My pineapple is finally flowering WOOOOOOOOOOO!
I can't even believe it. I didn't even notice it until I randomly looked over and took note that I probably need to water it.
I started this plant some 3-4 years ago now (after my previous one died due to exposure to the cold) and it's FINALLY bearing a flower (in the middle of frickin' winter no less!). I am very much looking forward to eating this pineapple
Browser MapReduce + Charting
Having dabbled in Mongo a bit, I was curious if there was an analog for MapReduce style functionality in browser JavaScript.
I found a small script as a starting point, but made some minor modifications to support array inputs:
// mapper should return an array of [{key:'somekey', value:'somevalue'}]
// reducer should return a single {key:'somekey', value:'somevalue'}
function mapReduce(i, mapper, reducer, property) {
var intermediate = [];
var output = [];
if(i && i.constructor == Array)
{
for(var x = 0; x < i.length; x++)
{
var value = i[x][property];
var key = property;
intermediate = intermediate.concat(mapper(key, value));
}
}
else
{
for (var key in i)
{
var value = i[key];
intermediate = intermediate.concat(mapper(key, value));
}
}
var groups = groupBy(intermediate);
for (var key in groups)
{
var values = groups[key];
output.push(reducer(key, values));
}
return output;
}
// list should be [{key:k, value:v}, ....] where key may be repeated.
// returns [{key, [v1, v2, v3...]}, ...] where key is *not* repeated.
function groupBy(list) {
var ret = {};
for (var i = 0; i < list.length; i++) {
var key = list[i].key;
var value = list[i].value;
if (!ret[key]) {
ret[key] = [];
}
ret[key].push(value);
}
return ret;
}
I then plugged in my custom map/reduce functions:
// Random data set -- could come from anywhere.
var data = [
{Country: "US", Type:"B", ProductCode: "001"},
{Country: "US", Type:"B", ProductCode: "001.A"},
{Country: "US", Type:"Z", ProductCode: "001.B"},
{Country: "UK", Type:"A", ProductCode: "002"},
{Country: "US", Type:"Z", ProductCode: "003"},
{Country: "FR", Type:"B", ProductCode: "003.A"},
{Country: "DE", Type:"B", ProductCode: "003.C"},
{Country: "DE", Type:"T", ProductCode: "004"},
{Country: "UK", Type:"R", ProductCode: "004.R"},
{Country: "UK", Type:"B", ProductCode: "005"}
];
// Custom mapper
function _map(key, value)
{
var result = [];
result.push({key:value, value:1});
return result;
}
// Custom reducer
function _reduce(key, values)
{
var sum = 0;
for(var i = 0; i < values.length; i++)
{
sum += values[i];
}
return {key: key, value: sum};
}
Now can I call it like so:
// Basic "group by - count" var out = mapReduce(data, _map, _reduce, "Country");
This yields the following result:
[{"key":"US","value":4},
{"key":"UK","value":3},
{"key":"FR","value":1},
{"key":"DE","value":2}]
Pretty nifty! And this data can then be fed into flot for charting purposes.
Sample (with flot example) is attached: MapReduce.zip
Externalizing Build Configuration
While PowerShell is great, I still do a lot of my build automation using plain old batch scripts when it comes to managing my SharePoint development workflow. Because I do my development outside of my SharePoint environment (develop on my host OS but SharePoint is in my guest OS), it requires some nifty batch scripts to keep the flow tight as no plugins or tools support this development model (dunno why...).
One of the challenges is making it easy to externalize environmental configuration information on a developer-by-developer basis.
In the past, I've relied on having team members create environment variables, but I decided to try out an external configuration file.
It worked out great
The following script reads a file and sets four variables:
@ECHO OFF ECHO ______________________________________________ ECHO Setting up build environment... ECHO ______________________________________________ For /F "tokens=1-4* delims=;" %%A IN (..\build-configuration.txt) DO ( SET SERVER=%%A SET LOGIN=%%B SET PASSWORD=%%C SET APPPOOL=%%D ) ECHO SERVER = %SERVER% ECHO LOGIN = %LOGIN% ECHO PASSWORD = %PASSWORD% ECHO APPPOOL = %APPPOOL%
And the file looks like this:
vm1;administrator;P@ssw0rd;SharePoint - DM App Pool
Now I can use the variables like so:
ECHO ______________________________________________ ECHO Copy to remote server... ECHO ______________________________________________ IF NOT EXIST \\%SERVER%\_ic\ctmo\ GOTO END DEL \\%SERVER%\_ic\ctmo\binaries /q /s XCOPY ..\..\pkgs\*.* \\%SERVER%\_ic\ctmo /Y /I /S
It even works when calling to psexec:
ECHO ______________________________________________ ECHO Start psexec... ECHO ______________________________________________ ::// Deploys the binaries only (doesn't reinstall .wsp). ..\..\tools\sysinternals\psexec.exe \\%SERVER% -u %LOGIN% -p %PASSWORD% -e -d -w C:\_ic\ctmo\binaries cmd /c \\%SERVER%\_ic\ctmo\binaries\gac-install.bat
The best part is that each developer only has to create the text file and all of the environment specific information (like the target SharePoint server) are configured in one tiny file, allowing the automation scripts to be ready for any developer.






