custom software development to help your business take flight™

Push to AppHarbor on every push to GitHub

Posted by Joe Wilson on Tuesday, October 11, 2011 1:21 PM

If you're using GitHub as your shared, online, git repository and you're using AppHarbor for continuous integration and deployment, you really have two git repositories - the GitHub one and the AppHarbor one.

AppHarbor instructions help you set up a remote to this git repository so you can push to it whenever you like, but that means you have two steps on most commits:

git push origin master

to push your code up to your GitHub repo, and:

git push appharbor master

to push your code up to the AppHarbor repo.  Because AppHarbor doesn't support SSH yet (only HTTPS is supported now), you also have to enter your password.  Not a giant hassle, but what if you could get rid of that last little bit of friction?

GitHub Service Hooks

GitHub Service Hooks run after a push to GitHub, and there is one to fire off your AppHarbor build/test/deploy.  It's under your GitHub repo > Admin > Service Hooks.

image

Once you're there, the instructions are pretty simple.

  1. Go to your application's main page on AppHarbor and find the "Create build URL". Example: https://appharbor.com/application/{application_slug}/build?authorization={token}
  2. "token" is the value of the "authorization" parameter.
  3. "application_slug" is your application's unique identifier.
  4. If your GitHub repository is private you need to add the "apphb" GitHub user as a collaborator. This enables AppHarbor to download the source code.

Here's where you can find the values for steps 1-3 in AppHarbor:

image

Step #4 can be skipped if you have a public repo.  If you're using private repos, add the "apphb" GitHub user to a GitHub team with "pull" grants.

All done!

Next time you do

git push origin master

your code will be pushed up to GitHub and AppHarbor will grab the latest code from GitHub (not from the AppHarbor repo anymore), build it, test it, and if the tests pass, deploy it.  All in one step!

Tags: , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Presentation: Rails vs .NET

Posted by Joe Wilson on Friday, August 26, 2011 10:12 AM

I had a lot of fun speaking at the inaugural Boulder Tech Throwdown last night.  The topic was Ruby on Rails vs. ASP.NET MVC.  Thanks to Ely Lucas for doing an awesome job (as usual) co-presenting with me on the ASP.NET MVC side.

The follow up panel discussion with the Ruby on Rails guys was really high quality, thanks to insights and comments from Collin Schaafsma and Ryan Cook.

Thanks also to Paul Sabini, Fernando Cardenas, and George Morris for organizing the meetup and for the invite.

The slides and code can be downloaded here.

Tags: ,
Categories: Presentations


kick it on DotNetKicks.com shout it on DotNetShoutOut

Sessionless MVC without losing TempData

Posted by Joe Wilson on Thursday, June 30, 2011 11:32 PM

I've blogged before about making your MVC controllers sessionless.  The knock on sessionless controllers is that TempData is off the table because it uses Session as its data store.

If you see the TempData name and dismiss it, thinking it's another dictionary like Session[] and ViewData[] and you're waaaay past that since your mister type-safe, take another look.  The neat thing about TempData is that it lasts for one more page load and then "poof" - it's gone.  It self destructs as soon as it has been read.

Sure, it's a dictionary with magic string keys, but it's a great place to store message like "Your changes were saved" when you redirect to another page.

So how do we get the benefits of TempData as an ephemeral application message store, but go all-in on the web farm with sessionless controllers?

Cookies!

I can hear you groaning.  Cookies are old school!  They can't hold much information!  They aren't secure!

Fine, don't put much in there, and don't put your secret fried chicken recipe in there.  We're just going to use this as a little string to tell the user everything is OK or it's not OK.

TempData uses a provider model for the data store, and Session is the default data store.  But you can get a cookie data store, CookieTempDataProvider, with Mvc3Futures NuGet package:

image

This should work as a TempData provider, but now it has to be wired into the application.  Here's how you can do that:

    public class BaseController : Controller
    {
        protected override ITempDataProvider CreateTempDataProvider()
        {
            return new CookieTempDataProvider(HttpContext);
        }
    }

Tags: , , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Running SpecFlow and WatiN tests with TeamCity and AppHarbor

Posted by Joe Wilson on Thursday, June 2, 2011 10:44 PM

If you're using SpecFlow and WatiN to automate your web testing, things work great on localhost, but tend to fall apart on the continuous integration server.  It's a "chicken or the egg" paradox.  If the new code hasn't been deployed yet, it can't be externally tested with WatiN, but it shouldn't be deployed until all the tests have passed.  What are you supposed to do?

TeamCity

If your SpecFlow and WatiN tests are in their own assembly (something like MyProject.AcceptanceTests.dll), and you're using TeamCity, it's easy to run your unit tests and integration tests, then deploy the code to a web server, then run your acceptance tests in a separate build step.  It's just a matter of breaking up the steps and designating which test assemblies will be run at which time.

This step in the screen shot below is running the unit tests and integration tests.  A separate, later step will run the tests in the **\*AcceptanceTests.dll assembly after the code has been deployed to the web server.

image

AppHarbor

imageIf you're using AppHarbor to run your tests on check in with "git push appharbor master", (and you should - it's super easy), you don't want to run your web tests until the code is deployed, but AppHarbor doesn't deploy anything until all the tests pass.

What we want is for AppHarbor to ignore the web automation tests until the code is deployed.  AppHarbor flips a *.config appSetting key named "Environment" to "Test" when it's testing and to "Release" when the code is deployed.  So while your tests are running, your App.config in your test project should look something like this:

<appSettings>
  <add key="Environment" value="Test"/>
</appSettings>

We can take advantage of this appSetting test runner change to tell SpecFlow to ignore this set of tests, which allows AppHarbor to continue running other tests and deploying the code.

This is not be the most elegant way to do this, but I got this working today:

[Binding]
public class MyStepDefinition
{
    [BeforeFeature]
    public void BeforeFeature()
    {
        if (ConfigurationManager.AppSettings["Environment"] == "Test")
            Assert.Ignore();
    }

    // Rest of step definition code...
}

The BeforeFeature attribute tells the SpecFlow test runner to execute that method before each feature.  With a quick check on the Environment appSetting value, we can tell AppHarbor to ignore this test and continue processing.  This will ignore all scenarios in all feature files.  Since all my scenarios are web automation tests, that works for me.

But what if you have some scenarios that rely on web automation testing and others that don't?  A more targeted approach would be to set a SpecFlow tag, like @web over the SpecFlow scenarios that require web automation testing to pass.  Then in the [BeforeScenario("web")] handlers for that tag, you could set Assert.Ignore to skip those scenarios while executing all other scenarios.

[BeforeScenario("web")]
public void BeforeScenario()
{
    if (ConfigurationManager.AppSettings["Environment"] == "Test")
        Assert.Ignore();
}

Once your non-web automation tests have passed and the code is deployed, you can point your local test runner at the AppHarbor URL and test against the newly deployed web app.

Tags: , , , , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Want to work on a green field ASP.NET MVC project?

Posted by Joe Wilson on Thursday, June 2, 2011 6:18 PM

I'm assembling a team to work with me on a green field ASP.NET MVC project.  We will be using ASP.NET MVC 3, an ORM (NHibernate or EF Code First), SQL Server 2008, dependency injection (Ninject or Castle Windsor), and ATDD and TDD (Specflow, Watin, NUnit, and Moq).  If you've done some but not all of these things, I'll teach you the rest to fill in the gaps.

This project will be run using lean methods, so we'll pull in work only when the current task is completely done, meaning the product owner has accepted it and it's ready to go live.  We'll spend a lot of time getting the acceptance criteria just right before we build anything, so there will be lots of small, just-in-time conversations with the product owner.

We'll have flexible hours and do a lot of remote work, but we'll get together for designing and pair programming several days a week, also.  This will be done face-to-face, ideally, but remote web-cam/desktop-sharing pairing is an option, too.  We'll have to tweak this as we go to be sure communication isn't slipping and the work is getting done to the customer's expectations.

For more details or to apply, please check out the careers section.

Tags: , , , , , , , , , , , ,
Categories: Business


kick it on DotNetKicks.com shout it on DotNetShoutOut

Why I'm using Moq instead of Rhino Mocks these days

Posted by Joe Wilson on Thursday, March 10, 2011 10:29 AM

On my last greenfield project I got to pick the unit testing and mocking tools.  I've been a regular Rhino Mocks user for a long time, but I've seen lots of code samples with Moq and liked the syntax, so I gave it a try.

I have to say, I did prefer Moq slightly to Rhino Mocks.  The newer Arrange, Act, Assert (AAA) style syntax in Rhino Mocks is a huge improvement over the old Record/Replay mess.  But Moq has the benefit of being born at the right time and has AAA style calls without the burden of supporting deprecated syntax.  This means Moq has cleaner documentation, fewer options, and fewer ways to get confused by the API.  I really couldn't find anything I needed to do in Moq that I couldn't do.  I did get a little tripped up having to get the object out of the mock, with myMock.Object calls, but that wasn't a big deal.

Here's the NuGet Moq install package syntax to add Moq to your unit test project:

image

I'm using mocking frameworks less these days.  Instead, I'll write a fake object myself or just try to avoid interaction testing altogether.  But if I needed to pull one off the shelf today, I'd grab Moq first.

Tags: , , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Fixing screen saver wait time of 999 minutes on a Dell laptop

Posted by Joe Wilson on Wednesday, March 9, 2011 11:12 PM

I have a Dell Precision M4500 with Windows 7 and ran into a problem with the screen saver not coming on.  Rebooting didn't fix it and calling Dell technical support didn't help ("You could reformat, I guess."). 

My screen saver dialog looked like this with 999 minutes in the wait time:

SNAGHTML1e594b9

If I clicked Preview, the screen would go blank like it was supposed to, so it wasn't a video card thing.  When I set the wait time to 10 minutes and clicked OK, then opened the dialog box again, my value of 10 minutes was saved, but the screen saver never turned on.

Finally, I found this dialog in the Dell System and Manager under Display Settings:

SNAGHTML1e9726b

See the "Disable alarms and timers for Presentations" check box?  I had that checked, so I unchecked it and the screen saver started working normally again.  I wasn't in Presentation mode, but this Dell app didn't seem to know that.

I hope this is helpful to someone stuck on the same issue.

Tags:
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Using CRM auto filtering in custom reports with complex SQL

Posted by Joe Wilson on Sunday, February 13, 2011 4:55 PM

Report prefiltering in Microsoft CRM 4.0 is a very cool feature - when it works.  The normal ways of setting up this auto filtering feature in your custom CRM reports are 1) special aliases, and 2) SQL strings.

Special aliases

The alias approach is the simplest one to use.  If you have a report that needs prefiltering, change your SQL from this:

select firstname, lastname 
from FilteredContact

to this:

select firstname, lastname 
from FilteredContact AS CRMAF_FilteredContact
The trick is the alias.  All you need to do is add an alias with"CRMAF_" in front of the view name, and CRM will rip out your SQL at report runtime and replace it with a custom SQL statement.  I think the acronym must stand for CRM Auto Filter.  By using a special alias CRM recognizes, the actual SQL run on your server will be similar to this, where contacts are prefiltered for city = 'Denver'.

select firstname, lastname 
from (select contact0.* 
      from FilteredContact as contact0 
      where address1_city  = 'Denver') AS CRMAF_FilteredContact

SQL strings

The other route you can go is mash together SQL strings and get a parameter from CRM that is the prefilter SQL statement.  You've probably done this kind of this kind of thing before:

declare @sqlstring varchar(max)
set @sqlstring = 'select firstname, lastname 
from (' + @CRM_FilteredContact + ') AS MyFilteredContacts '
exec (@sqlstring)

This really isn't bad when you have a small SQL statement, but you probably wouldn't need to create a custom CRM report if it was a simple SQL statement, right?  The CRM users could create their own simple report inside CRM for that.

When the SQL statement gets complex, you have quote and string bugs.  When the SQL gets lengthy, you can't have over a certain number of characters in the SQL string or you have to split it and you end up with:

exec (@sqlstring1 + @sqlstring2 + @sqlstring3)

You can get it to work, but it's pretty yucky. 

The problem

I much prefer the alias approach.  It's much easier to read, and the aliases are not too much trouble to add in and get prefiltering in CRM for free.

The problem is, the alias technique breaks down when the queries get more complex or you need multiple datasets in your report using the same prefiltering.  Here are the cases I've found and the workarounds.

Dataset based on a union query

If you have a query with a union statement, like:

select firstname, lastname 
from FilteredContact AS CRMAF_FilteredContact
where address1_city = 'Denver'
union
select firstname, lastname 
from FilteredContact AS CRMAF_FilteredContact
where address1_city = 'Highlands Ranch'

CRM will do the alias filtering trick, but only for the first select.  The second select in this query will not be filtered at all.

The fix I've come up with uses SQL temp tables.  Maybe not the best approach, but I had a lot of reports that were not auto filtering as expected, and this has worked so far:

select * 
into #FilteredContact
from FilteredContact AS CRMAF_FilteredContact

select firstname, lastname 
from #FilteredContact
where address1_city = 'Denver'
union
select firstname, lastname 
from #FilteredContact
where address1_city = 'Highlands Ranch'

drop table #FilteredContact

This let's CRM do what it wants to, and swaps out your SQL where it finds the CRMAF_FilteredContact alias the first time.  It also let's you leave the rest of your SQL pretty much intact.

Dataset based on multiple queries selecting into a temp table

I had another query that was suffering the same problem, but this query was a little different.  I needed the data displayed in columns, so I created a temp table and populated it with selects.  There were multiple selects in the query filling up the columns in this temp table.  Everything ran great without auto filtering.

But with auto filtering, the same thing happened.  The first select statement got filtered.  The ones after that were untouched.  The fix I used was the same.  I created a temp table, populated it with the auto filtered data, then selected off that temp table throughout. 

To be a good TempDB citizen, I dropped the temp table at the end of the query.  SQL Server does the temp table drop for you when the table is out of scope, but it helps to have the drop statement in there when testing the query in SQL Server Management Studio and running it multiple times in the same query window.

Reports with multiple datasets

This technique works if you have a complex query that needs auto filtering.  But what if you have multiple datasets in your report, and they all need to be auto filtered?

In one report, the queries were very similar.  I had two datasets that were identical expect one used report parameter A and one used report parameter B.  This was to compare two funnels for two different time periods.  Auto filtering worked fine on the first dataset and funnel, but didn't do anything on the second dataset or funnel.

The fix in this case was to create one dataset that didn't filter on either report parameter A or parameter B.  Then in the report, have the funnel controls themselves do the filtering based on the report parameters.  Most SQL Server Reporting Services data controls have a filter property.  I've never really used them before now, since it almost never makes sense to pull all the data from the database and then filter inside the report.  I'd rather filter on the database first.  However, in this case, the control filters helped and I got the result I wanted because there was one dataset with one CRMAF_ alias.

On another report, there were three charts built using three different datasets.  The datasets were the same except for the group by clause.  I was able to use the same approach and condense these to one dataset with no group by clause, then do the grouping in the chart controls.  Again, it made me feel a little bad that the data wasn't already summed and grouped when it got to the chart control, but at least it was working.

Tags: , ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Autocomplete dropdown with jQuery UI and MVC

Posted by Joe Wilson on Friday, February 4, 2011 11:03 AM

You know the autocomplete dropdown.  It has become ubiquitous, and customers now expect them in their web apps.  "Can't you just set autocomplete=true or something?"  It's not quite that easy with ASP.NET MVC, but it's pretty simple.

We want to set up a text box on a web page that uses jQuery to call back to a controller action as the user types.  The action method will return a JSON array of values that match what the user has typed so far.  Each time they type and pause for a second, the screen should show the new matching records.

The view

First, we'll work on the view.  Set a reference to jQuery and to jQuery UI.  These are included with new MVC 3 projects (look in the Scripts folder), or you can find a CDN and reference them there.  I've got this in my view's head section:

<head>
    <title>Autocomplete Test </title>
    <script src="<%: Url.Content("~/Scripts/jquery-1.4.4.min.js")%>" type="text/javascript"></script>
    <script src="<%: Url.Content("~/Scripts/jquery-ui.min.js")%>" type="text/javascript"></script>
</head>

Then you need a text box to tie all this too.  The search results will be shown under this text box to give the appearance of a dropdown.  This simple form should do:

<form action="/Search/ProcessTheForm" method="post">
    <input id="searchTerm" name="searchTerm" type="text" />
    <input type="submit" value="Go" />
</form>

Next, you'll need to use the autocomplete function in jQuery UI like this:

<script type="text/javascript">
    $(function () {
        $("#searchTerm").autocomplete({
            source: "/Search/AutocompleteSuggestions",
            minLength: 3,
            select: function (event, ui) {
                if (ui.item) {
                    $("#searchTerm").val(ui.item.value);
                    $("form").submit();
                }
            }
        });
    });
</script>

Here we've got a jQuery selector binding the autocomplete function to the searchTerm input box("#searchTerm").  We then set the URL for the source for the autocomplete suggestions, which in this case, will be our controller (Search) and action method (AutocompleteSuggestions).  Then we set the number of characters the user has to type before we make the first call.  It's set to three here to prevent pulling back meaningless matches from searching too soon.

Finally, we set up the autocomplete function's select event to set the selected value from the dropdown list as the value in the searchTerm input box and submit the form to the Search controller and the ProcessTheForm action method.

Here's the entire view for reference:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

<!DOCTYPE html>
<html>
<head>
    <title>Autocomplete Test</title>
    <script src="<%: Url.Content("~/Scripts/jquery-1.4.4.min.js")%>" type="text/javascript"></script>
    <script src="<%: Url.Content("~/Scripts/jquery-ui.min.js")%>" type="text/javascript"></script>
</head>
<body>
    <form action="/Search/ProcessTheForm" method="post">
        <input id="searchTerm" name="searchTerm" type="text" />
        <input type="submit" value="Go" />
    </form>
    <script type="text/javascript">
        $(function () {
            $("#searchTerm").autocomplete({
                source: "/Search/AutocompleteSuggestions",
                minLength: 3,
                select: function (event, ui) {
                    if (ui.item) {
                        $("#searchTerm").val(ui.item.value);
                        $("form").submit();
                    }
                }
            });
        });
    </script>
</body>
</html>

The controller

Now you need to write something that will return the records that match what the user has entered so far.  This might be a database call with a LIKE searchTerm + "%" (watch out for SQL injection, though) or SOUNDEX or a web service call.  Be sure you limit your returned search results to maybe the top 10 matches so you don't bog things down with too much data moving over the wire.

Next, set up a controller action to invoke this back-end call and JSON up the returned list.  We've already names the controller (Search) and action (AutocompleteSuggestions) in our view.  Something like this should work, where _searchRepository is my repository call that finds matching strings, which are then converted to JSON:

public JsonResult AutocompleteSuggestions(string term)
{
    var suggestions = _searchRepository.GetAutoCompleteSearchSuggestion(term);
    return Json(suggestions, JsonRequestBehavior.AllowGet);
}

Don't forget the JsonRequestBehavior.AllowGet parameter in the Json call.  You won't get anything back from the action method if you leave it off.

Also, the name of the parameter passed into the action method is important.  The jQuery UI autocomplete method will call your source URL with a query string like "?term=foo".  If you set the parameter name to a string named "term", the MVC model binder will grab the value from the query string for you.

The jQuery UI Autocomplete function has several other options and events you can use in your app, and the demos are worth checking out too.

Tags: ,
Categories: Technical


kick it on DotNetKicks.com shout it on DotNetShoutOut

Buy, Build, or Both?

Posted by Joe Wilson on Monday, January 31, 2011 11:34 AM

You need to open up a new market, cut costs, or automate a process. New software can help solve this problem, but which software meets your needs?

There are three main categories of software: 1) packaged software, 2) software as a service (SaaS), and 3) custom software. This article covers the pros and cons of each type of software and can help you make the right purchase decision for your business.

When is packaged software or SaaS the right choice?

If you want a word processor, email client, or accounting package, it's been done. The products are numerous, mature, and range from free to very reasonably-priced.

More and more business software is falling into this commodity space, such as enterprise content management solutions, document management programs, and customer relationship management (CRM) software. These tools have a single purpose and usually very good at what they do.

Cost wise, buying software is usually cheaper than building custom software. As we'll see later on, there are some caveats to this, but this conventional wisdom is often correct.

This is where most software evaluations end. It meets your requirements and it's cheaper. Make the purchase and move on, right? Well hold on a sec.

There are some risks to packaged software and SaaS that are often overlooked:

  • They often include modules you don't want and may not be an exact fit for your business. Adding the modules you do want is extra. So is implementing the software and customizing it to fit your business.
  • They work in functional silos and are usually from multiple vendors. You've got to be sure it will integrate with your other systems.
  • They may not let you get the data out the way you want it.
  • Your users may not be as excited about your choice as you are.

Let's take a closer look at these problems and when they may ultimately push your decision to the custom software side.

Get just what you want, no more, no less

Buying software "off the rack" is the way to go if the package meets all your business requirements. But most of the time, you're buying functionality you don't need and missing functionality you do need. You shouldn't have to change your business to match the software you've just purchased.

The common answer is to have consultants implement a solution and customize it to your business. If the packaged software is a good fit with your requirements, this can be a good choice, too. You just want the consultants to come in and round out the corners, though. They shouldn't need to rewrite anything; just configure it for your organization and maybe integrate your new software with some of your existing systems.

But at some point (and it should be pretty soon after the install), the consultants should finish and go home. If they need to stick around for lots of customizations or integrations, you may have lost the justification for buying instead of building it. And don't forget that all those customizations can't just be thrown out when the vendor rolls out the next version of the product you've bought. Too many companies make this mistake and fall into a cycle of perpetually customizing packaged software.

With custom software, the program meets all the requirements of the business and doesn't include any modules you don't want. It usually costs more initially, but because you buy only what you need, nothing more, you won't need to perpetually customize.

Get deep integration with your existing systems

With packaged software, integration with your other systems ranges from simple to a painful implementation project. For example, if you purchase CRM software and want to load all your existing customers into it, the tool should provide this. Most consultants can do this work quickly. Painful integrations are when you can't get the data out, or you can get it out, but you have to use the vendor's very strange API to pull just one record at a time.

If the new system is one piece of a larger process, or if it will be the system of record for your enterprise data, you will have many integration points for data coming into and out of the new system.

With custom software applications, those integration points are built in because they are part of the original requirements. The software integrates deeply with your current enterprise systems.  If done right, the new integration APIs are written in standard ways that are discoverable by any new applications that may come online in the future.

How stable is that vendor? What is your exit plan?

The main value proposition of packaged software is the functionality you get for the price you pay. But that value can go out the window if the vendor goes out of business or is purchased and no longer supports your software. And what happens if you get their software deeply embedded in your business and the vendor goes up on price next time you're negotiating their contract. Vendor lock-in is a real business risk.

In a struggling economy, it's not unusual to see smaller software vendors bought out by larger ones. It's also common to see start-up SaaS companies offering low introductory prices to get customers on board, then gouging them once the cost of leaving that vendor gets high enough. When purchasing software, consider the long term costs and how you would get your data out.

And don't just make a plan. You need to test that your plan really works and you can get the data out when you need it and in a format that is useful to you. This is like a building evacuation plan. Usually no one reads the plan until the emergency happens, and then it's too late. You have to actually test pulling your data out in a non-emergency situation to see if the plan will really work.

With custom software, your business owns all the code and all the data from the start. Most systems are turned over to IT departments after the construction phase, so there is no risk of your data being held hostage.

Although unlikely, it's certainly possible to have your custom software development vendor go out of business or be purchased by a competitor during the development phase. However, if the system is written using your IT department's standards and your application development vendor is releasing working versions of code every few weeks, you minimize vendor lock-in or vendor going-out-of-business risk.

Where is the data stored?

With packaged software, the data is often stored in a proprietary database. Even if it's in a more common database format, many vendors will not support direct queries or reports against the data store.

If you're using SaaS and the data is in the cloud, you can't be sure where it is. That works great for some organizations that really want the redundancy and availability, but for sensitive data or for businesses that are heavily regulated, data security will be a deciding factor in your purchase.

Custom software puts the data where the organization wants it because access to and location of the data are part of the requirements. It's as secure, redundant, and available to queries and reports as the requirements dictate.

Get user buy in

The typical IT buying process is to identify the requirements and find the packaged software that best fits. This evaluation is often handed over to a technical person to be sure the software is a fit for the organization from an IT perspective.

The problem comes when a package is selected and the users are told that starting next Monday, they will be in training, and starting the Monday after that, they will be using the new system. If the users haven't helped choose the software, they may reject it or sabotage the implementation.

With custom software and custom integration projects, users are involved in the requirements early on, and the new system should be tailored to their specifications. They are the most critical stake holders and can set the tone for a successful adoption throughout your organization.

Summary

Software purchase decisions can be as complex as the problems they attempt to solve. Decision makers need to consider short and long term costs and risks of buying packaged or SaaS software as is, buying it and customizing it to integrate into the business, or going with custom application software the whole way. The easy answer is not always the best answer when all the risks are considered.

Tags:
Categories: Business


kick it on DotNetKicks.com shout it on DotNetShoutOut

Image Details