Tech in the 603, The Granite State Hacker

Project to Help Feed Starved Kids – Jimmy Sudoku v4

UPDATE Feb 6, 2014:  http://granitestatehacker.kataire.com/2014/02/jimmy-sudoku-5-orange-edition.html

Click here to view in the Windows Phone App Marketplace.  (the app is ‘free’ with the $5 donation via purchase.  All proceeds go to #NoKidHungry.)

I’ve had a lot of fun learning the quirks of Silverlight for WP7 devices, and after playing with some of the other WP7 Sudoku’s out there, I’m very glad to have Sudoku done my way on my phone again. 🙂  I’m even more psyched that I can share my app with others, as it’s now Certified by Microsoft and available on the Windows Phone App Marketplace!

I have to admit… there’s a lot of competition out there for Sudoku’s on WP7 devices, and probably not a huge demand… I realize that for as much fun as I had polishing this app,  probably the best way to find its value is to “give it away”.  

Scan this Tag with your Windows Phone to find Jimmy Sudoku
Scan this Tag
with your phone to find
Jimmy Sudoku
in the
Windows Phone
App Marketplace

So my family & I considered some options and decided on a national organization that helps feed under-fed/malnourished children here in the US. I thought it was a fitting thing… building software is my strength, and this is a way I can share it.   I also can think back to some exceptionally lean times when I was a kid… memories I’d love to help avert for others if any way possible.  So, yes, all proceeds from paid downloads will be donated to that cause.

I finally had a chance to re-do the old .NET 2.5 Compact Framework-based app as a Silverlight 4 app for Windows Phone. I’ve been toying with this release for months (since November 2011, actually… I pulled down Visual Studio Express 2010 for Windows Phones around then, anyway).  

In case you’re wondering… I asked the charity if it was ok to use their name and maybe a logo or something, but out of respect for their other national-market beneficiaries who have already done this, I would have to also guarantee a sizable donation.  100% is the best I can do, but that’s not necessarily a sizable donation, so I can’t use any trademarked IP, which, as I understand it, includes their name.  (I’m glad I asked.  🙂    While I can’t do this now, I’m hoping that if I play by the rules and manage to make a decent contribution with it, that I’ll earn the privilege and kick the project up a notch.  (In the meantime, I wonder if the twitter hashtag #NoKidHungry counts…. )

You can also click here on just about any device to get to the Marketplace site.


Finally, please visit the Facebook Page, and feel free to Like and/or Share posts to help spread the word!  🙂

Anything you can do to help is appreciated!

Tech in the 603, The Granite State Hacker

Choen the counterBot

I’m pretty excited… both my kids seem interested in computers and what you can do with them. My daughter, especially, is very interested in learning bits and pieces about programming. My son is interested, but not so much in the nuts & bolts of programming. He is interested in numbers and performance, though. They’re both young, though… he’s in 1st grade, and she’s in 2nd.

She’s expressed some interest in programming before. Last time we talked about application design… she got extremely ambitious and drew about twenty pages of “designs” (hand-drawn mock-ups of screens) for games that she wanted to create. (Essentially knock-offs of games she’s played on favorite websites like PBS Kids and Pixie Hollow.)

More recently, she was using my computer, and discovered that when she’s logged into my computer on her account, she has access to Visual Studio Express. When she asked what it was, I told her it was tools to write programs, and she just lit up. Immediately, she wanted to start working on a simple UI that mimics a math program she uses at school.

I started to realize, however, that she needs to learn more of the basics before we dive into a full fledged user interface. I started to think about some of the earliest programs I learned to write.

Back when I was in 4th grade, I got my hands on an Apple II, and one of the first programs I learned to write on it was a program that accepted two numbers (start and finish) and simply scrolled out a count between them.

Nowadays, we have object oriented programming, and C#, but I wanted to try to keep it simple and understandable for her. I decided that we would create a “counter-bot” class, and we would create an instance of it named whatever she wanted it to be named (She decided on Choen (pronounced more like Cohen)).

Today, we got the basics of the counterBot class written, and wrote a short program to create an instance of it, initialize it, and start counting, sending the numbers out to the screen. A countBot has beginAt, finishAt, countBy properties, and Start, Count, GetCurrentNumber and isFinished commands.

Here’s the code:
counterBot.cs:
——————————————————–


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Choen_the_counterBot
{
class counterBot
{
public int beginAt;
public int finishAt;
public int countBy = 1;
private int currentNumber;

public void Start()
{
currentNumber = beginAt;
}

public int GetCurrentNumber()
{
return currentNumber;
}

public void Count()
{
currentNumber = currentNumber + countBy;
}

public bool isFinished()
{
bool result=false;
if (currentNumber > finishAt)
{
result = true;
}
return result;
}

}
}

——————————————————–

and here’s the console Main method in program.cs:
——————————————————–


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Choen_the_counterBot
{
class Program
{
static void Main(string[] args)
{
counterBot Choen = new counterBot();
Choen.beginAt = 0;
Choen.finishAt = 2000000;

Choen.Start();
DateTime startTime = DateTime.Now;

while (!Choen.isFinished())
{
Console.WriteLine(Choen.GetCurrentNumber());
Choen.Count();
}

Console.WriteLine("Finished counting from "
+ Choen.beginAt.ToString()
+ " to "
+ Choen.finishAt.ToString()
+ " by "
+ Choen.countBy
+ "'s in "
+ (DateTime.Now - startTime)
.Seconds.ToString()
+ " seconds!");

Console.ReadKey();
}
}
}

——————————————————–

I got a bit fancy and added the timer code to see how long it took. Together, my son, daughter, and I watched Choen the counterBot count out various scenarios, and got as ambitious as watching it count from zero to two million. In order to get my son’s interest, I added the timer to see how long it took. We discovered that my computer can count from zero to two million, “out loud” (showing the numbers on the screen) in 52 seconds. If we hid the console writes, it could do it in less than a second.

Next time we play with it, we’ll write a graphic UI (a windows form) and hook Choen the counter bot up to it.

Tech in the 603, The Granite State Hacker

WORKAROUND: Misconfigured Windows-Integrated Authentication for Web Services

In trying to drive a process from a SharePoint list, I ran across a problem…

I couldn’t create a web reference in my C# project due to some really weird problem… In the “Add web reference” wizard, I entered my URL, and was surprised by a pop-up titled “Discovery Credential”, asking me for credentials for the site.

Since I was on the local domain and had “owner” permissions to the site, I thought I would just waltz in and get the WSDL.

Ok, so it wants creds… I gave it my own.

Negative…!?!?

After a few attempts and access denied errors, I hit Cancel, and was rewarded by, of all things, the WSDL display… but I still couldn’t add the reference.

After quite a bit of wrestling, it turns out there was an authentication provider configuration problem. The site was configured to use Kerberos authentication, but the active directory configuration was not set up correctly. (I believe it needed someone to use SetSPN to update the Service Principal Name (SPN) for the service.)

One way to resolve the problem was to set the authentication provider to NTLM, but in my case, I didn’t have, (and wasn’t likely to get) that configuration changed in the site (a SharePoint Web Application) I really needed access to.

In order to make it work, I had to initially create my reference to a similar, accessible site.

(e.g. http://host/sites/myaccessiblesite/_vti_bin/lists.asmx )

Then, I had to initialize the service as such, in code:


private void InitWebService()
{
System.Net.AuthenticationManager.Unregister("Basic");

System.Net.AuthenticationManager.Unregister("Kerberos");

//System.Net.AuthenticationManager.Unregister("Ntlm");

System.Net.AuthenticationManager.Unregister("Negotiate");

System.Net.AuthenticationManager.Unregister("Digest");


SmokeTestSite.Lists workingLists = new SmokeTest.SmokeTestSite.Lists();

workingLists.Url = "http://host/sites/mybrokensite/_vti_bin/lists.asmx";

workingLists.UseDefaultCredentials = true;

workingLists.Proxy = null;

lists = workingLists;
}

What this accomplishes is it unregisters all authentication managers in your application domain. (This can only be done once in the same app domain. Attempts to unregister the same manager more than once while the program’s running will throw an exception.)

So by having all the other authentication managers disabled in the client, the server would negotiate and agree on Ntlm authentication, which succeeds.

Tech in the 603, The Granite State Hacker

Reading SharePoint Lists into an ADO.Net DataTable

[Feb 18, 2009: I’ve posted an update to show the newer technique suggested below by Kirk Evans, also compensating for some column naming issues.]

The other day, I needed to write some code that processed data from a SharePoint list. The list was hosted on a remote MOSS 2007 server.

Given more time, I’d have gone digging for an ADO.NET adapter, but I found some code that helped. Unfortunately, the code I found didn’t quite seem to work for my needs. Out of the box, the code missed several columns for no apparent reason.

Here’s my tweak to the solution:

(The ListWebService points to a web service like http://SiteHost/SiteParent/Site/_vti_bin/lists.asmx?WSDL )

private data.DataTable GetDataTableFromWSS(string listName)

{

ListWebService.Lists lists = new ListWebService.Lists();

lists.UseDefaultCredentials = true;

lists.Proxy = null;

//you have to pass the List Name here

XmlNode ListCollectionNode = lists.GetListCollection();

XmlElement List = (XmlElement)ListCollectionNode.SelectSingleNode(String.Format(“wss:List[@Title='{0}’]”, listName), NameSpaceMgr);

if (List == null)

{

throw new ArgumentException(String.Format(“The list ‘{0}’ could not be found in the site ‘{1}'”, listName, lists.Url));

}

string TechListName = List.GetAttribute(“Name”);

data.DataTable result = new data.DataTable(“list”);

XmlNode ListInfoNode = lists.GetList(TechListName);

System.Text.StringBuilder fieldRefs = new System.Text.StringBuilder();

System.Collections.Hashtable DisplayNames = new System.Collections.Hashtable();

foreach (XmlElement Field in ListInfoNode.SelectNodes(“wss:Fields/wss:Field”, NameSpaceMgr))

{

string FieldName = Field.GetAttribute(“Name”);

string FieldDisplayName = Field.GetAttribute(“DisplayName”);

if (result.Columns.Contains(FieldDisplayName))

{

FieldDisplayName = FieldDisplayName + ” (“ + FieldName + “)”;

}

result.Columns.Add(FieldDisplayName, TypeFromField(Field));

fieldRefs.AppendFormat(“”, FieldName);

DisplayNames.Add(FieldDisplayName, FieldName);

}

result.Columns.Add(“XmlElement”, typeof(XmlElement));

XmlElement fields = ListInfoNode.OwnerDocument.CreateElement(“ViewFields”);

fields.InnerXml = fieldRefs.ToString();

XmlNode ItemsNode = lists.GetListItems(TechListName, null, null, fields, “10000”, null, null);

// Lookup fields always start with the numeric ID, then ;# and then the string representation.

// We are normally only interested in the name, so we strip the ID.

System.Text.RegularExpressions.Regex CheckLookup = new System.Text.RegularExpressions.Regex(“^\\d+;#”);

foreach (XmlElement Item in ItemsNode.SelectNodes(“rs:data/z:row”, NameSpaceMgr))

{

data.DataRow newRow = result.NewRow();

foreach (data.DataColumn col in result.Columns)

{

if (Item.HasAttribute(“ows_” + (string)DisplayNames[col.ColumnName]))

{

string val = Item.GetAttribute(“ows_” + (string)DisplayNames[col.ColumnName]);

if (CheckLookup.IsMatch((string)val))

{

string valString = val as String;

val = valString.Substring(valString.IndexOf(“#”) + 1);

}

// Assigning a string to a field that expects numbers or

// datetime values will implicitly convert them

newRow[col] = val;

}

}

newRow[“XmlElement”] = Item;

result.Rows.Add(newRow);

}

return result;

}

// The following Function is used to Get Namespaces

private static XmlNamespaceManager _nsmgr;

private static XmlNamespaceManager NameSpaceMgr

{

get

{

if (_nsmgr == null)

{

_nsmgr = new XmlNamespaceManager(new NameTable());

_nsmgr.AddNamespace(“wss”, “http://schemas.microsoft.com/sharepoint/soap/”);

_nsmgr.AddNamespace(“s”, “uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882”);

_nsmgr.AddNamespace(“dt”, “uuid:C2F41010-65B3-11d1-A29F-00AA00C14882”);

_nsmgr.AddNamespace(“rs”, “urn:schemas-microsoft-com:rowset”);

_nsmgr.AddNamespace(“z”, “#RowsetSchema”);

}

return _nsmgr;

}

}

private Type TypeFromField(XmlElement field)

{

switch (field.GetAttribute(“Type”))

{

case “DateTime”:

return typeof(DateTime);

case “Integer”:

return typeof(int);

case “Number”:

return typeof(float);

default:

return typeof(string);

}

}

Tech in the 603, The Granite State Hacker

The Great Commandment

While I was writing a post the other day, I noticed that I had neglected a topic that I find very important in software development. Risk management.

There are only a few guarantees in life. One of them is risk. Companies profit by seizing the opportunities that risks afford. Of course, they suffer loss by incidents of unmitigated risks. All our government and social systems are devices of risk management. In business, risk management is (now, and ever shall be) the great commandment.

Many software engineers forget that risk management is not just for PM’s. In fact, software and its development is fundamentally a tool of business, and, by extension, risk management. The practice of risk management in software really extends in to every expression in every line of source code.

Don’t believe me? Think of it this way… If it wasn’t a risk, it would be implemented as hardware. I’ve often heard hardware engineers say that anything that can be done in software can be done in hardware, and it will run faster. Usually, if a solution is some of the following…
· mature,
· ubiquitous,
· standard,
· well-known,
· fundamentally integral to its working environment

…it is probably low risk, particularly for change. It can likely be cost-effectively cast in stone (or silicone). (And there are plenty of examples of that… It’s what ASIC’s are all about.)

Software, on the other hand, is not usually so much of any of those things. Typically, it involves solutions which are…
· proprietary,
· highly customized,
· integration points,
· inconsistently deployed,
· relatively complex / error-prone
· immature or still evolving

These are all risk indicators for change. I don’t care what IT guys say… software is much easier to change than logic gates on silicone.

I’ve dug in to this in the past, and will dig in more on this in future posts, but when I refer to the “great commandment”, this is what I mean.

Tech in the 603, The Granite State Hacker

Compact and Full .NET Frameworks

One of the things I’ve been intrigued by for a while now is the fact that code compiled for the .NET Compact Framework (all versions) executes very nicely on the full .NET Framework.

For example, my personal hobby project, “Jimmy Sudoku”, is written in C# for the .NET Compact Framework 2.0. There are actually two install kits. The first is a .CAB file for Windows Mobile devices. The second is an .MSI for Windows 9x, XP, and Vista. The desktop install kit even serves two purposes. First, it installs the program on the desktop. Second, it leverages ActiveSync to push the .CAB up to the Windows Mobile device.

It’s a .NET Compact Framework app especially for Windows Mobile devices, but many ‘Jimmy’ fans don’t have a Windows Mobile device to run it on.

The coolest part is the ease in which all of the components inter-operate. The .EXE and .DLL’s that are delivered to the mobile device are the very same as the ones that are delivered to the desktop. Like Silverlight to WPF, the Compact Framework is a compatible subset of the full framework, so interoperability is a given.

Even better, you can reference CF assemblies in Full framework assemblies. One immediate offshoot of this in my hobby project… the web service I built to service “Game of the Day” requests actually references the CF assembly that implements the game state model and game generator code. The assembly that generates games on Windows Mobile PDA’s & cell phones is the very same assembly that generates games in the ASP.NET web service.

Admittedly, there are some bothersome differences between the CF and the Full .NET Framework. The CF does not support WPF. The CF has no facilities for printing. Also, while the CF does supports some of the common Windows Forms dialogs, it does not support File Save and File Open dialogs on Windows Mobile Standard Edition (Smart Phone / non-touchscreen) devices.

These differences can be overlooked to some extent, though, for the fact that one compiled assembly can execute on so many very different machine types. Further, with interoperability, one can extend a CF-based core code with full-framework support. For example, I’m currently playing with desktop print functionality for my hobby project.

Something that I’d really love to see, some day, is a good excuse to develop a Windows Forms app for a client that had shared components between the desktop and a mobile.

I can imagine that this model would be superb for a huge variety of applications, allowing a fully featured UI for the desktop version, and an excellent, 100% compatible, very low risk (almost “free”) portable version.

I’ve often thought this would work great for apps that interface hardware, like:
field equipment,
mobile equipment,
vehicles of all sorts,

…simply plug in your PDA (via USB or Bluetooth), and it becomes a smart management device for the equipment, using the very same code that also runs on the desktop.

Tech in the 603, The Granite State Hacker

Jimmy SuDoku 3.0 Released

Those of you who have worked with me on a project in the past few years probably know of my hobby project. It’s an implementation of SuDoku. It’s made for Windows Mobile devices (cell phones, etc.), but it also runs on Windows XP (et al).

The old version, 2.5, had been published on PocketGear. This last update was published in January, 2007, just before I started with Edgewater.

I’ve been hacking at it here & there since then, but the project suffered from lots of maladies… most significantly lack of time.

So after more than a year and a half, I’m happy to finally announce Jimmy SuDoku 3.0!

3.0 has a whole new game state model, based on CLR classes rather than an XML DOM. This means the puzzle generator’s fast enough on hand-held devices that it doesn’t need a web service to do the work for it. Another side-effect of this change is a smaller run-time memory footprint, though I’m not sure by exactly how much.

I also figured out how to leverage the hardware controls on WM6.0 & 6.1 devices so that non-touchscreen devices can play, too.

Tech in the 603, The Granite State Hacker

Art & Science

Fire & Ice… Day & Night…

This question, Art vs. Science, has come up a million times in software development circles. Reading Paul Johnson’s (Paul’s Pontifications) blog post, in conjunction with a discussion in the Tech Mill at Edgewater, (thanks, Jason!) I have come to see that art and science are not as opposite as I once viewed them to be.

What hit me was that Paul makes the statement that there’s no process to implementing software. I still disagree. There are many processes.

The number of processes that an implementer can choose from to write his/her code is often vast, and depends on the problem set. A problem set includes many things, including requirements, tools, target platform, development platform, existing code, and even the implementer’s mood and frame of mind. That is what makes implementing code, like painting, or creating a recipe, an art.

Within a common implementation problem set, there can be a large number of processes which can be applied to derive valid solutions. In fact, there are so many, that some distinct processes may actually render the very same code. So, to be more clear, it’s not that there’s no process… it’s that there’s no single valid process.

Knowing that there’s no one single valid process doesn’t mean that we can’t pick a needle from the haystack… if the process produces a solution within the problem set, it’s good.

Now consider what happens when you start to narrow a problem set. There’s lots of things you can do. Frameworks, platforms, clear-specific requirements, best practices, coding standards, well structured architectures… these things are all factors that limit the problem set. By narrowing a problem set, you narrow the number of valid processes. By narrowing the number of valid processes that a developer can choose from, lots of interesting things start to happen. You achieve more predictable results, and are more likely to achieve repeatable schedules… and you reduce overall project risk.

This is what’s so interesting about contemporary trends in software development, such as Ruby on Rails… use of these tools narrows problem sets that developers face. This means the implementer can spend less time figuring out where the blanks are, and more time filling them.

Now let’s take this further. What happens when you reduce the problem set dramatically…? Take a single, relatively well known problem, on a very specific platform, using a very small set of unambiguous expressions. You get a very tightly defined process. By doing this, you wring the art out of creating something, to the point where it becomes machinable. The process becomes realized as a factory.

So to answer the question… Art or Science?

It’s a trick question… art and science are not exclusive opposites. Art is about freedom to choose your creative process. Science is about knowing what processes are available, and the pros and cons of each. So programming, like all creative activities, is usually art (except in single-processed cases), and usually science (except in cases of serendipity and true miracles).

Paul’s Pontifications: An Under-Appreciated Fact: We Don’t Know How We Program

Tech in the 603, The Granite State Hacker

Multiprocessing: How ’bout that Free Lunch?

I remember reading an article, a few years back…

The Free Lunch Is Over: A Fundamental Turn Toward Concurrency in Software

Its tagline: “The biggest sea change in software development since the OO revolution is knocking at the door, and its name is Concurrency.”

Mr. Sutter’s article suggests that because CPUs are now forced to improve performance through multi-core architectures, applications will need to typically employ multi-threading to gain performance improvements on newer hardware. He made a great argument. I remember getting excited enough to bring up the idea to my team at the time.

There are a number of reasons why the tag line and most of its supporting arguments appeared to fail, and in retrospect, could have been predicted.

So in today’s age of multi-core processing, where application performance gains necessarily come from improved hardware throughput, why does it still feel like we’re getting a free lunch?

To some extent, Herb was right. I mean, really, a lot of applications, by themselves, are not getting as much out of their host hardware as they could.

Before and since this article, I’ve written multi-threaded application code for several purposes. Each time, the threading was in UI code. The most common reason for it: to monitor extra-process activities without blocking the UI message pump. Yes, that’s right… In my experience, the most common reason for multi-threading is, essentially, to allow the UI message pump to keep pumping while waiting for… something else.

But many applications really have experienced significant performance improvements in multi-processor / multi-core systems, and no additional application code was written, changed, or even re-compiled to make that happen.

How?

  • Reduced inter-process contention for processor time
  • Client-server architectures (even when co-hosted, due to the above)
  • Multi-threaded software frameworks
  • Improved supporting hardware frameworks

Today’s computers are typically doing more, all the time. The OS itself has a lot of overhead, especially Windows-based systems. New Vista systems rely heavily on multi-processing to get performance for the glitzy new GUI features.

The key is multi-processing, though, rather than multi-threading. Given that CPU time is a resource that must be shared, having more CPUs means less scheduling collision, less single-CPU context switching.

Many architectures are already inherent multi-processors. A client-server or n-tier system is generally already running on a minimum of two separate processes. In a typical web architecture, with an enterprise-grade DBMS, not only do you have built-in “free” multi-processing, but you also have at least some built-in, “free” multi-threading.

Something else that developers don’t seem to have noticed much is that some frameworks are inherently multi-threaded. For example the Microsoft Windows Presentation Foundation, a general GUI framework, does a lot of its rendering on separate threads. By simply building a GUI in WPF, your client application can start to take advantage of the additional CPUs, and the program author might not even be aware of it. Learning a framework like WPF isn’t exactly free, but typically, you’re not using that framework for the multi-threading features. Multi-threading, in that case, is a nice “cheap” benefit.

When it comes down to it, though, the biggest bottlenecks in hardware are not the processor, anyway. The front-side bus is the front-line to the CPU, and it typically can’t keep a single CPU’s working set fresh. Give it a team of CPUs to feed, and things get pretty hopeless pretty quick. (HyperTransport and QuickPath will change this, but only to the extent of pushing the bottle necks a little further away from the processors.)

So to re-cap, to date, the reason we haven’t seen a sea change in application software development is because we’re already leveraging multiple processors in many ways other than multi-threading. Further, multi-threading options have been largely abstracted away from application developers via mechanisms like application hosting, database management, and frameworks.

With things like HyperTransport (AMD’s baby) and QuickPath (Intel’s), will application developers really have to start worrying about intra-process concurrency?

I throw this one back to the Great Commandment… risk management. The best way to manage the risk of intra-process concurrency (threading) is to simply avoid it as much as possible. Continuing to use the above mentioned techniques, we let the 800-lb gorillas do the heavy lifting. We avoid struggling with race conditions and deadlocks.

When concurrent processing must be done, interestingly, the best way to branch off a thread is to treat it as if it were a separate process. Even the .NET Framework 2.0 has some nice threading mechanisms that make this easy. If there are low communications needs, consider actually forking a new process, rather than multi-threading.

In conclusion, the lunch may not always be free, but a good engineer should look for it, anyway. Concurrency is, and will always be an issue, but multi-core processors were not the event that sparked that evolution.

Tech in the 603, The Granite State Hacker

Corporate Specialization

There’s an old adage: “Anything you can buy, you can produce yourself for less.”

In our business, we’re well aware that there are a few fundamentally flawed assumptions with that sentiment. Despite the barriers to entry and many other recurring costs, somehow the idea seems pervasive in the business world.

I started my career in a consulting shop that insisted it was a product company. Then I moved, through market forces, into products based companies. I stayed on board with some of them long enough to help shut out the lights and unplug the servers when the sales didn’t hit the marks. The other big problem I’ve seen with product shops was that it’s engineering group typically went through its own “release cycles”… once the product was released, my team was cut to the bone.

I’ve never been in a classic IT shop, per se, but I’ve definitely been on tangent IT-support projects within “product” oriented companies. In IT groups, I’ve often thought that companies might see IT projects as frills and overhead. At some level, I think the pervasive IT-alignment is a counter measure to that idea. Still, it seems IT projects are typically viewed as liability, rather than asset. When it comes time for the company to refocus on its core competencies, (which is inevitable in the ups & downs of business) IT projects are prime targets for cutbacks.

Since the “.COM bust”, in these types of companies, an engineer on the staff for just three years is often seen as a “long-timer”… but no one feels bad about that, since a lot of these companies fold in that time frame, as well.

After experiencing the product based companys’ ups & downs, and seeing many colleagues who had already been through the IT shops, I’m convinced… the outsourced project / consulting route is really the wave of the future, even more than it has been in the past. It’s only a matter of time before business admits this necessity as well.

It makes sense… I wouldn’t figure on every company to own & operate their own power plant.

Why should they typically want their own software engineering group if they don’t have to?

[Edit: See also Part 2 Here]