I got involved in a discussion a few weeks ago with someone who was pondering why – generally speaking – a competent Access application developer tends to have a lower pay range than an equally competent .NET application developer. My initial response was “well hey, look at all the different frameworks involved in .NET dev, look at the overhead, the architectural requirements of putting together an OOP app…” so on an so forth. The discussion itself brought on a lot of pondering on my end… yes, I understand that as a net result, .NET takes longer and is generally more complex, but why so? What are the real differences in how a competent Access application developer approaches a project versus how a .NET LOB application is put together? Is it just because .NET projects tend to have teams because there’s more to do, and thus higher costs? Is it just because there’s more code in general required to produce an equivalent .NET application? Is it just because you spend some more money (or more time) on some control suites to make .NET development a little easier? Is it just because you spend more time styling a .NET application’s chrome than you do in Access?
While all of those things are true and do tend to drive the cost of .NET development higher than a comparable Access application, they’re not the real answer. Rather, they don’t answer the real question: yes, a .NET application costs more because they require more work, but why – really – does someone who does all this for C# make more than someone who does it for Access? It’s not just because it takes longer – someone making $500 a day isn’t making anymore than someone making $1000 in two days.
Is there a skillset that a C# developer has that an Access developer doesn’t? Not really, at least in terms of writing code. The C# language is just as good as any other, and a great C# coder is no more or less efficient than a great VBA coder. Frameworks? That was my initial reaction, but no, that’s not right either. A great Access developer can breeze through the DAO and Access OMs and automate Excel, Outlook and whatever else just as well as a C# developer can make use of whatever frameworks and namespaces they’re using. That’s just a matter of knowing where to go to get a job done. Nothing special going on there – the two are entirely comparable.
Is a C# developer a “better” developer because he has to spend more time writing a Data Access Layer or working with an ORM than an Access developer who has excellent data access and binding available to him? Nope. Just takes longer.
What about styling as an example… if an Access developer is good at skinning an application and it takes him a few hours to get everything in place, is that any different than a designer spending four times as long working up style templates for WPF? Is the latter somehow “better” because his job takes longer? No, not at all – that’s just the nature of skinning a WPF application, it just takes longer – that doesn’t mean that one dev or the other is better or worse as a developer, so the pay scales ought to be the same, right? Right…
So, what’s up? I don’t think too many people would argue that great .NET developers do in fact tend to get paid a bit more than great Access developers. What’s the deal?
That’s the skill. That’s the one that drives the real answer to this question. The fact of the matter is, architecting an Access application really isn’t all that difficult, but properly architecting a .NET application takes a ton of experience and a lot of hard lessons.
As Access developers, we’re all used to the fact that before we start any coding or table creation, etc, we ought to analyze all the requirements and plan some things out. This is nothing new to anybody, at least not to “professional” Access developers. And with .NET, we need to do all that too, no getting around it. To further understand the importance of this, let’s consider the difference between making architectural changes to an Access application vs. that of an OOP application.
Access applications, being non-OOP, are relatively flat applications whose code is generally quite linear. It’s usually very easy (or at least relatively easy) to see how changes to X might impact entity Y. Following code paths and coming up with dependencies between them is a straightforward task, even with highly complex Access applications. That’s one of the great things about procedural languages. OOP code on the other hand much more resembles something with a dimension of depth. Instead of a nice flat application architecture that we tend to see with Access, we’ve got this three dimensional applications that has layers within layers and the simple means of just plain knowing what’s going on all around becomes considerably more difficult. Following code paths aren’t all that much more difficult, but understanding the implications can very well be. It’s very much like this: as Access developers, we had beaten into our brain that Access data is not like Excel data: Excel data is a 2d spreadsheet, and Access data is a 3d database. The analogy compares quite well to code structures in VBA vs. .NET (or any procedural vs. OOP language).
So, there’s one key aspect, though in reality this in itself is just laying the groundwork for why putting together a .NET application is so much more difficult and requires a much higher skillset. The thing here is that when you’re only working with a couple of C# classes to get a task done, it’s really no more or less difficult than working with a module or two in VBA to get something done. So at a higher level view, we’ve got added complexity just due to the very nature of OOP programming, but on a task-level view, it’s no biggie really.
So how do we deal with the much more complex layers of code in OOP? Well, first of all, many of us moving from Access to .NET don’t even really recognize the problem we’re about to run into. We don’t really realize that we can’t just “wing it” so to speak, even when we are very, very good at putting together very clean Access applications. Most good Access developers are self taught, and as such tend to move into OOP in self-teaching mode as well. So, we pick up Visual Studio, we play around a little bit, maybe get used to the C# language, do a few little test utilities, feel pretty good about ourselves, then set out to write an actual application. What happens next is that partway through what we’re trying to do (despite how well we had previously tried to plan), we’ve got this giant, ugly nasty mess on our hands, because we didn’t realize that in order to do anything right in OOP and keep things straight and maintainable, we absolutely need to use some patterns.
This requirement isn’t apparent at first. It takes a lot of work in learning OOP itself, how to tweak it around, a lot of research and brainpower just getting used to this new programming style, and once we’re confident we have a handle on it, we start trying to put something together. Then we need to know how to do this, or that, and we stop and think about it, and we try to do it, or we realize that the way we’re doing it just isn’t going to work because… well, because OOP only works in certain ways, and you really can’t brute force it the way you can in procedural languages. So after umpteen hours where we thought we were making progress, we have to stop, take a step back, and regroup.
This is usually where we find out out the importance of using design patterns in OOP. It’s hard to learn this importance before we made this mess, because it’s very hard to imagine this mess that we’ve made without having actually done it before (like trying to explain to someone who isn’t a parent what being a parent is like… it’s damn near impossible). So we go out and grab a copy of GOF Design Patters and maybe Head First Design Patterns (because it’s generally easier to understand than the GOF book, though the GOF is the best design pattern reference once you have the basics). We pick them up and read, and try to apply, and our heads hurt from it because it’s a whole new world, and we toy around and read more and re-read and try to apply, and we go back and refactor our mess (usually starting a brand new slate and borrowing ideas from our old mess) and we slowly learn how to use design patterns and start to see how with OOP, we’re working with composite units in conjunction with each other to create functionality, which is entirely different than what we had to do with Access.
And we’re not even close to done yet.
So, finally armed with a decent understanding of design patterns, we set out into OOP again, feeling great about our newfound (and hard earned) skills: “alright, awesome, I’ve got this shit down now, let’s get back to it!”. We go, we code, we think, we code more, we get passed where we did last time, things look far, far better, we code more, we think, we think a little harder, we code a little more, we think even harder, and we realize that… well, things just don’t seem right. The code doesn’t feel clean. It’s moving forward, all that stuff back there looks real good, but now I’m trying to push through this next wall and I just can’t seem to get it. We code a little more, and a little more, and we realize that we’re digging ourselves into a nasty, ugly hole again. This one’s a little further up the road, but it’s just as ugly.
What the hell happened now? Well, now that we’ve understood the basics of how real OOP is done and we’ve realized that we should be working to interfaces instead of implementations and we’re pulling these classes together and they’re all nice and composite and things are looking relatively good behind the scenes… now we’ve come to this point where trying to pull together the next level of things – typically trying to maintain good separation of concerns between the various data business and presentation layers, starts to get a little messy. What we also realize is that – even though we’ve got a good understanding of basic design patterns now – the .NET platform for building UI applications really forces us to use some sort of higher level pattern for the UI. Typically this is either MVC or MVVM.
Ok, well my core OOP skills are good and I know what I need to do, but this damn platform is forcing me to use MVVM and I don’t really know it yet, but I know that the way I’m going now is seriously working against the current and things are far, far far more difficult than they should be.
So, we set out to look up information on MVVM (or MVC, whichever – MMVM for Silverlight/WPF, MVC for ASP.NET), and we try to make sense of that, and it’s basically about as much of a pain in the ass as trying to wrap our heads around design patterns was. Now we’re dealing with high level architectural patterns. The worst part about this is trying to figure out in your own head what’s what, because when you google info on the topic, you’re going to see so many conflicting viewpoints about how they’re used that it’ll make you sick to your stomach (this, in itself, should say something about the mess that OOP can be, but I digress).
Now we’ve got a new major problem to solve, and just as before, it’s one that we can’t fully understand and appreciate before we screw it up, so, much like before, we’re stuck taking a big step back, reading, applying, re-reading, re-applying, so on and so forth.
(This is not easy stuff to learn on our own – we’ve already come a long way from when we first opened Visual Studio and spit out our little calculator utility and say “hey cool, I can program .NET!”. If you’re working as a mid-level C# developer, you don’t have to worry entirely about this because you’re more or less told what to do and how to do it – you don’t have to be the one responsible for putting together how everything works).
Anyway, carry on. Many more hard lessons, lots of trial and error to see and fully realize what to do and what not to do. Good for you… now you understand the real importance of MVVM and all of a sudden WPF’s fucked up data binding makes perfect sense, and it’s even easy to use now that we’re starting to form some semblance of an architectural design. Moving forward! We’re good to go now, Godspeed!
But that’s not the end either. It’s probably more fair to call that the end of the beginning, because once you *finally* have all of these “basics” down, you can start to actually think about how to properly build a .NET LOB application. At least now we know our way around OOP and we can make some reasonably intelligent decisions on what to do to get a job done. From there, we can finally start to work on our own style, work our own experience, and get to that point where we know, as well as we can for Access, that “yes, we can do it this way and it’ll work, but this way is much easier and has [whatever] advantages.”
That whole mess – learning how to really make use of OOP – in itself should provide some insight as to why .NET development tends to take a considerably higher level of skill than most Access developers have ever really needed. Keep in mind now that at this point, on the .NET end of things we’re dealing with concepts that many “plain old .NET developers” don’t necessarily need to get into. Sure, they’re apt to have a basic understanding of it all, but they’re not required to *really* know, inside and out, all of the pros and cons and horrible implications of making the wrong decisions. They’re mostly responsible for writing code and plugging into predefined pieces of the architecture.
So here’s the thing. We as Access developers know the problems created by a bad design decision when modeling the data. We know that if we mess up in a normalization decision and we need to fix it later, it’s going to cause some pretty major problems. It’s a very critical area. When this happens, most times (assuming we don’t realize until much later), the easiest way to deal with it is to hack something else in there, because going back to change it would require such application-wide impact that your client will never pay for it and you certainly don’t have the time to re-write an application because of it. That’s generally the worst that we have to face as far as bad design decisions in Access. Everything else can be gotten around pretty easily, and even with a bad data design decision (as long as it’s not too bad), we can usually hack something into place that’s maybe not the prettiest but gets the job done and everyone comes out happy enough.
With a .NET program architecture, if you make a wrong design decision, you’re fucked. OOP absolutely resists any sort of hacks like that. It breaks the entire OOP model, it destroys your carefully thought out design patterns and there’s simply no forcing MVVM to “mostly MVVM except for this part here that we screwed up and have to beat into the mix of things.” That’s really, really, really hard to do with OOP. With procedural programming it’s pretty easy, so we can get away with it, but not in OOP. To make matters even worse, by the time we realize our bad design decision in OOP, it’s usually far enough along where fixing it (the only real option), is rebuilding a lot of things that shouldn’t have had to be rebuilt.
So now we begin to see why .NET is a far more disciplined are of application development than Access programming is. We can understand how much we need to learn to even get to a point where we can somewhat model a real application, and we can further see how slight mishaps in our decisions can have horrifying effects on the project.
How many times does a person need to make these mistakes before they’re at a point where they can claim to be “very good” at OOP application architecture? Experience is gained by doing things wrong and learning from them, and doing more things wrong and learning still. Consider that a great many OOP programmers get out of college and start being a code monkey somewhere, they don’t have to learn all this stuff. They have to learn how to work with frameworks, sure, but not this architecture stuff.
The ability to cleanly architect a .NET LOB application is an extremely valuable skill, and the difference between someone who knows what they’re doing and someone who’s fumbling their way though it can be a key factor in the success of a project (or a whole company, for that matter). Application architecture is quite a prestigious position, because it’s such a highly valuable skill.
Access application developers don’t need (and don’t tend to have) that type of hard earned experience. The implications of screwing up are far softer and are not likely to destroy a project or company.
A good C# developer doesn’t make any more money than a good Access application developer, but a good OOP application architect has skill levels far above and beyond what is ever a requirement in Access, and their ability to use that experience and do it right is what makes a project successful.
And that’s why the pay rates for the higher positions of .NET development are more than what Access developers tend to see.
(and I haven’t even touched base on project management…)