Asp.net Developer: Why we didn’t hire you.

Photo by striatic (Creative Commons)

In the last year, I’ve interviewed around 30 candidates. These were for mid to senior level asp.net positions at three different companies. Sadly, I’ve only recommended hiring two. Each case is unique, but here are some common reasons for a thumbs-down vote.

You don’t know JavaScript:

Depressingly, none of the candidates had a strong background in JavaScript.

You want a web development job, but can’t code in the only language web browsers can run? Tell me more!

Ten years ago we mostly avoided JavaScript, and just did everything on the server. You could scrape by without knowing much JavaScript. Five years ago, I would have been worried about your lack of commitment amid the rising popularity of *.js frameworks, but if you were otherwise qualified I would still risk it –hoping you could learn enough javascript on the job to keep up.

Today, JavaScript is everywhere. It’s in the browser. It’s on the server. Some of your developer tools and utilities use JavaScript. JavaScript is the native language in many enterprise-grade database systems. JavaScript has become a basic requirement for just about any kind of development, and it isn’t an easy language to work with. Mastering the techniques, patterns, and frameworks needed for production quality code takes a lot of time and practice.

So, if you walk into my interview without a solid grasp of JavaScript, you aren’t going to get the job. You simply aren’t qualified to code for the web.

Never used distributed source control:

I get that your skills with DVCS are thin. Most business employers aren’t using a DVCS yet. Some don’t use formal source control at all (which is terrifying). Most candidates had a little experience with SVN or TFS, but a full third had no source control experience at all. Only a handful had used Mercurial or Git, and several hadn’t even heard of either.

I can teach the basics of DVCS to any competent user, even a non-developer, in just few hours. I’m not worried about the lack of the skill itself –a PluralSight video and a copy of SourceTree and you’ll be good to go. But the fact that you don’t already have significant experience with at least one popular DVCS is a major red-flag.

The entire asp.net stack, most of the popular 3rd party libraries, and even most of the developer tools are open source projects, hosted on the web in public Git or Mercurial repositories. Most build systems, package managers, and continuous integration systems rely on DVCS technologies. The fact that you aren’t comfortable with at least one such system tells me that you aren’t keeping up with your profession, and aren’t participating in the general developer community.

You don’t know anything about design patterns.

When you list Asp.net MVC on your resume, I absolutely will ask about design patterns. My expectations are low, but I will still be disappointed if you have no clue what I’m talking about.

I’m fine that you don’t know Martin Fowler’s name, or about the Gang of Four. If I ask you to name an example of an abstract singleton factory in asp.net, I won’t be surprised when you to look at me like I just broke out in interpretive dance. But I do expect you to know what the term “design pattern” means. You should know that “MVC” is the name a design pattern — even better if you can explain a little about the pattern itself.

I’m not an academic design pattern guru myself, but some patterns are so common that it is difficult to discuss code with someone who doesn’t know the basic terminology –singleton, factory, repository, observer, etc.

Asp.net development in particular revolves around a very specific set of design patterns; IoC, MVC, Repository, and Unit of Work being the most relevant. If you aren’t at least vaguely aware of these patterns, then you can’t possibly be proficient with the asp.net MVC framework.

You don’t have to be able to debate the merits of domain driven design vs. onion architecture. You don’t have to be able to tell me the distinction between transaction script and unit of work. But if I ask you if a C# static class is a singleton, you should at least understand the words coming out of my mouth… even if you can’t give me a good answer to the question itself.

Day laborer:

You only know technologies your former employers used, but nothing else. Your last company still used ADO.NET Datasets, but you haven’t even bothered to read up on LINQ to SQL, Entity Framework, or NHibernate?

Congratulations, you did the bare minimum necessary to earn a paycheck!

I want candidates that take responsibility for developing their own professional skills beyond just the minimum. It’s great that you can meet today’s challenges, but I need people who will be ready for tomorrow’s projects too.

Needs training wheels:

You’ve only coded modules for existing applications, and maybe a few stand-alone tools or utilities for the server room. What you’ve done sounds impressive, but it doesn’t seem like you’ve ever built an application from the ground up. I’m not expecting that you’ve architected your own custom enterprise, multi-tenant ERP solution. But nowhere in the interview did you give me the impression that you’d ever even clicked “file –> new project” either.

So you can follow someone else’s patterns and conventions, and you can plug code into someone else’s framework. But I can’t trust you to code for problems beyond those that some pre-built framework anticipated.

If I give you a blank Visual Studio solution and a list of requirements, can you deliver a complete and high-quality product? Will it be coded to standards? Will the code be organized and maintainable?

Closed shop:

You’ve never build software for users outside your employer’s firewall, much less for the general public. The quality, reliability and usability needs of software for non-technical end-users is different. It requires a more disciplined approach, and involves skills that aren’t heavily used with purely internal projects.

If you can code for the public end-user, I am confident that you can easily code for my company’s internal users. The reverse is not true.

Line of business developers are particularly prone to this problem, since most LOBS are internal only. Internal users are more tolerant to errors, poor design can be offset by specialized user training, and reliability is bolstered by your control of both the server and client environments.

I need to know that you can code to higher standards when it is necessary.

Doesn’t know why:

You have a firm grasp of the tools and technologies your employers have used in the past, but when I ask, “why did your company choose to use X instead of Y?” I get nothing. You told me about that amazing widget you wrote — I liked that story — but when I ask you why you didn’t use a 3rd party widget instead, you can’t give me a valid business justification for the extra time and effort you spent on a custom solution.

You don’t have to be an expert in cost analysis or anything, but I need to know that you can make sound decisions about the technologies, platforms, and coding techniques that you’ll use to solve the challenges my company is facing. Choosing a technology just because it’s cool or popular isn’t always the best bet for business applications.

Entity Framework – Storing a complex entity as JSON in a single DB column

jsonDuring the development of TicketDesk 2.5, I came across an unusual case. I wanted to store a large chunk of my entity model as a simple JSON string, and put it into a single column in the database.

Here’s the setup:

I have an entity that encapsulates all of a user’s display preferences and similar settings. One of those settings is a complex set of objects that represents the user’s custom settings for list view UI pages. There can be many lists, each with separate settings. Some of the settings for a list contain collections of other objects, resulting in a hierarchy of settings that goes three levels deep

I didn’t want to represent these settings as a relational data model in the database though. Using EF’s standard persistence mapping conventions, this collection of settings ends up being spread across six  tables. The TSQL queries to access that data would be rather slow and cumbersome, and the relational model doesn’t add any value at all.

Instead, I just wanted to serialize out the entire collection of settings as a single JSON string, and store it in one column in the user settings table. At the same time though, I wanted the code behave as if this were just a natural part of my regular EF entity model.

The solution:

The solution was to use a complex type, with some fluent model binding magic, to flatten the hierarchy into a single column. The heirarchy itself is represented as a custom collection, with a bit of manual JSON serialization/deserialization built-in.

I got a pointer the right general direction from this SO post, which saved me a bunch of time when approaching this more advanced scenario.

First, let’s take a look at the root entity here:

public class UserSetting
 {
 	[Key]
    public string UserId { get; set; }

    public virtual UserTicketListSettingsCollection ListSettings { get; set; }
}

This is the only entity which will map to its own table in the DB. The ListSettings collection is the property I want persisted as JSON in a single column.

Here is the custom collection that will be stored:

public class UserTicketListSettingsCollection: Collection<UserTicketListSetting>
{
    public void Add(ICollection<UserTicketListSetting> settings)
    {
        foreach (var listSetting in settings)
        {
            this.Add(listSetting);
        }
    }

    [JsonIgnore]
    public string Serialized
    {
        get { return Newtonsoft.Json.JsonConvert.SerializeObject(this); }
        set
        {
            if (string.IsNullOrEmpty(value))
            {
                return;
            }

            var jData = Newtonsoft.Json.JsonConvert.DeserializeObject<List<UserTicketListSetting>>(value);
            this.Items.Clear();
            this.Add(jData);
            
        }
    }
}

This is a collection type, and is inheriting generic Collection<T>. In this case, T is the UserTicketListSetting type — which is a standard POCO wrapping up all of the settings for all of the various list views in one place.

Some of the properties inside UserTicketListSetting contain collections of other POCOs. The specific details of what’s going inside those classes doesn’t matter to this discussion, just understand that it results in a hierarchy of related objects. None of the properties in that hierarchy are marked up with EF attributes or anything.

The only magic here is that we have a Serialized property, which manually handles converting from/to JSON. This is the only property that we want persisted to the database.

To make that persistence happen, we will make UserTicketListSettingsCollection an EF complex type, though not by using the [ComplexType] attribute. Instead, we’ll manually register this complex type via the fluent model builder API.

In the DB Context this looks like this:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
	modelBuilder.ComplexType<UserTicketListSettingsCollection>()
        .Property(p => p.Serialized)
        .HasColumnName("ListSettingsJson");
}

This just tells EF that UserTicketListSettingsCollection is a complex type, and the only property we care about is the Serialized property. If there were other properties in UserTicketListSettingsCollection, you would need to exclude them with something like:

modelBuilder.ComplexType<UserTicketListSettingsCollection>().Ignore(p => p.PropertyToIgnore);

And that’s all I needed to get EF to store this entire hierarchy as a single JSON column.

Using this model in code is just like using any other EF entity. I can query it with LINQ expressions, and SaveChanges on the DbContext updates the JSON data in DB just like any other entity. Even the generation of code-based migrations works as expected.

It took a LOT of experimentation and digging to figure out how to make this work, but the implementation is rather simple once you know how to approach the problem.

This also reflects the amazing power and flexibility of Entity Framework. EF can be extended to fit very advanced scenarios even when the designers didn’t anticipate them directly.

You can see the full implementation of this in TicketDesk 2.5. Currently, TD 2.5 is in alpha, so look to the develop branch in source control on CodePlex. You will find this example, as well as several variations in the TicketDesk.Domain assembly.

TicketDesk 2.5 – Coming soon!

TicketDesk-2.5While I’ve been working on TicketDesk 3, the code for TicketDesk 2 hasn’t been getting any younger. Since TD3 is still a ways out from a production release, I’ve decided to release a major overhaul of TicketDesk 2 in the meantime. The new TicketDesk 2.5 release will bring the technology stack of TD2 up to the latest version of Microsoft’s Web Platform. Several of the changes will be derived from code developed for TD3.

I am targeting the end of October for a beta version, with a final release in mid to late November (subject to change, as always).

Here are the major changes I have planned so far:

Remove AD Security:

This release will not support direct integration with Active Directory. This was a popular feature in TicketDesk 1 and 2, but it has also been a major problem area as well. Instead, TD 2.5 will only support AD indirectly, via federation (ADFS for example) or through integration with an external identity server (like Azure AD).

Modernized Local Security:

TicketDesk 2.1x still uses the ancient SqlMembership providers that shipped with .Net 2.0 back in 2005. Authorization and identity have come a very long way since then, so TD 2.5 will be upgraded to the newest Aspnet.Identity framework version. It will also provide on-screen management tools to help administrators migrate existing user accounts from their legacy TD 2.1x databases.

UI Changes:

TD2.1x was built on Asp.net MVC 2, with the original Asp.net view engine. This isn’t very well supported by recent versions of Visual Studio, and most developers have long since abandoned the old engine in favor of Razor. I don’t plan many major changes to the general UI’s behavior or appearance, but by re-implementing on Razor I can bring the project into compatibility with Visual Studio 2013 and current Asp.net coding standards.

There will be some minor changes to the UI. I will be removing some of the crusty old JQuery components, and updating the styles to take advantage of newer CSS features that weren’t widely supported when TD2 was first built.

Entity Framework Code-First:

TicketDesk 2.5 will move from Entity Framework 4 database-first models to Entity Framework 6 with a Code-First model. EF Migrations will provide ongoing schema management from here out, including the ability to migrate legacy TicketDesk 2.1x databases. Along with this change, TD 2.5 will also include on-screen database management utilities for migrating legacy databases, seeding demo/test data, and so on.

This refactoring will also bring TD 2.5 in line with the technologies backing TD 3, which will greatly simplify future upgrades.

Eliminate MEF:

The managed extensibility framework has continued to evolve, but it still isn’t a very good choice for Dependency Injection concerns in a web application. Instead, TD 2.5 will use Simple Injector for IoC. Some of the simplifications in the back-end should also reduce the reliance on dependency injection techniques quite a lot.

Improved Email Notifications:

Several improvements to Email Notifications are planned. Most of these are intended to give administrators greater control over how and when TicketDesk sends email notifications. This will include better on-screen testing and diagnostic tools for troubleshooting email related issues.

Multiple Projects:

TicketDesk 2.5 will support multiple projects, which let you handle tickets for different operations, projects, or products in isolation. You will be able to move tickets from one project to another, and can turn off multiple project support  entirely if you don’t need the functionality. I do not know yet if I’ll support user permissions on a per-project basis in this version, but TD3 will certainly provide that functionality.

Watch/Follow Tickets:

Users will be able to watch or follow tickets without having to be the ticket’s owner or assigned user. This will allow these users to receive notifications of changes as the ticket progresses.

Azure Deployments:

TicketDesk 2.5 will be deployable as an Azure WebSite. Currently, there are several issues that make deploying to cloud or farm environments tricky. The biggest are that the Lucene search indexes are stored on the file system, and the old SqlMembership providers are not compatible with the features provided by Azure SQL. These issues are not insurmountable for experienced .Net developers, but deployment to web farms or cloud providers is not currently an out-of-box capability of the system.

To make TicketDesk play well with cloud environments, a pluggable storage provider will be used for any features needing access to non-database storage. When deployed to single instance environments, TD 2.5 will use the file system, but you will be able to reconfigure it for Azure Blob storage when deploying to the cloud. Attachment storage will be moved out of the SQL database to the new storage provider as well.

The only hold-up for Azure SQL is the membership system, but the newer Aspnet.Identity framework fully supports EF migrations and is compatible with any EF data provider –including Azure SQL.

Early pre-alpha code is already committed to the CodePlex repository, and will be updated regularly as work continues. Right now, there isn’t much to see. I’m still working on the basic plumbing for database and identity management, so there are no TD specific user facing features yet. As soon as the back-end is shored up, I’ll start porting in TD specific UI features.

A demo version of the site will be available soon, hosted on Azure. I just have to workout a few minor details related to resetting the database and providing sample seed data, then I can open the demo to the public.