forge

Why JavaScript Makes Bad Developers

I’ve bumped into JavaScript from time-to-time through my professional career and up until recently I’ve been able to keep a relatively safe distance from it. I always had a developer who I could assign to do the JavaScript stuff. It was generally a relatively small amount of the overall framework and it was easy enough to assign to a junior developer to work on. However, more recently it’s become a more “real” part of the overall development experience. Therefore, I felt like it finally became something that I had to get neck deep into. What I’ve found is that it’s changed a lot over the years and it’s picked up a lot of scars.

My goal for this post is to help the developer to help development managers understand what they’re asking their developers to do, to talk about how the techniques for writing JavaScript teach bad habits, and address that history is repeating itself.

Rewind: Object Oriented Programming and Agile

When I was first starting development object oriented programming wasn’t an assumption. We were working with non-object languages like FORTRAN and COBOL. We were dealing with C – before the ++ was added. In fact for years object oriented programming was treated with skepticism. I can vividly remember doing a full object architecture for an ecommerce site when the project manager/engagement manager came back to me freaking out that the performance was bad. By the way, bad was some pages at 5 seconds to load. Anyway, he was all up in arms that we were going to have a slow site and that it would never be responsive. The discussion ended with me upset – knowing that it was trivial to address the issues – and him insisting that I prove that the code could perform. It took me about 2 hours to insert the caching and list-object generation into the framework and to reduce our page load times to sub-second. His skepticism was removed – and I turned off all the caching so that we could do the rest of our development and debugging.

The initial reports with object oriented programming was that it was a better system. The initial research seemed to show that switching to an object oriented approach improved developer productivity. However, the push-back on these studies was that the developers who were doing the object oriented development were better developers. Given that Fred Brooks in his classic work The Mythical Man-Month quoted developer productivity rates can vary by up to 10 times between two developers, the improvements in object oriented development could easily have been explained by better developers.

I should say that I don’t believe that a language causes better development practices, rather, I believe that it encourages them. Back in 2004 I wrote an article “What does Object-Oriented Design Mean to You?” talking about how the language doesn’t make you do the right things. However, it can encourage the right things. Kurt Lewin, a German-American psychologist, said that behavior is a function of both the person and the environment. That is that behavior can be explained by some interaction between the person and their environment. You can put a good developer in a bad situation and they’ll succeed. You can put a bad developer in a good situation and they’ll succeed – and the reverse is also true. So while I’m making the point that languages influence good or bad behavior, and later I’ll make the point that our habits are formed by our behaviors, I should say that our goal is to encourage the right behaviors to develop the right habits.

A similar argument about improvement was made in the early days of agile development practices. It was 6 years ago when I was investigating how agile was supposed to be done. (I read and reviewed numerous classical software development books and agile development books: Agile & Interactive Development: A Manager’s Guide, The Rational Unified Process Made Easy: A Practioner’s Guide to the RUP, Peopleware, The Psychology of Computer Programming, Agile Software Development with Scrum, Agile Software Development, Dynamics of Software Development, and Software Craftsmanship – The New Imperative) The arguments against agile fell into two basic categories. The first was that agile was just an excuse to not document things. Numerous teams wanted to try agile by following the steps without understanding the principles and did end up floundering and putting a bad taste in the mouths of large organizations. This lead to the second argument – we don’t’ believe it works. This was placed in opposition to research that seemed to show that it did work. Teams wrote more code, had fewer defects, and the customers were happier with the end solutions. However, the argument was that the best developers worked on these projects and therefore they would have done better whether or not the methodology was better – or not.

Let me flip this argument around and say that I’m convinced that there are some truly gifted developers who will be able to make any technology, methodology or approach work. I believe that it’s absolutely possible for development in JavaScript to be done well by a select few developers. I’m just not sure that these magical developers are either plentiful nor do I believe they are in your organization. (Present company excluded, of course.)

So by drawing parallels to object oriented programing and agile methodologies, I’m not saying that JavaScript is a better approach, in fact, I think you’ll find by the end of the post that I believe it’s not a step forward but rather a fairly large step backwards – I’m simply illuminating that I’m aware of history and how change is necessary to make improvements – however, it has to be the right change. I know where we’ve been in this craft we call software development and I believe I know a step backwards when I see it.

Centralized, Decentralized, and Back

One of the subtle shifts that’s been happening for a long time is the move of processing power from a centralized authority to the client and back again. We started with “dumb” terminals. Whether you were working in an IBM world and knew the numbers 3270 (mainframe terminal) and 5250 (mini-computer terminal) or whether you were looking at a terminal starting with VT (Unix, VAX, etc.) you were staring at a very simple device that did little more than relay keystrokes and receive character updates. The shift came when PCs started landing on users desks. While they were being used to access the host computers they started running applications locally –and those applications seemed more responsive and more in-tune with the way that the user wanted to use the machine.

So we started to pull applications off the centralized processing platform on to the relatively immature PCs. We got ISAM databases like Dbase and FoxPro. We started developing “trivial” applications from the point of view of the central processing host but invaluable from the users point of view. As the PCs got connected to local networks we started the client-server revolution. In this time we used the PCs to do the processing and interaction and had centralized data storage.

It wasn’t long, however, before we discovered that some operations are best done right were the data sits. Enter databases like btrieve and the start of SQL databases. Ultimately the pendulum was swinging back from the client to the server as we moved more and more logic back to the server side for reliability, consistency, and stability. By the time the Internet was becoming popular we had moved most of our development back to core servers. We had intelligent interaction on PCs but all of the meaningful processing happened on the central server.

Internet browsers were created as a way to navigate information that was linked together. Over time users began demanding richer experiences from the web and over time we started enhancing what we did on the client again. We developed plug-ins that operated like mini-operating systems running in the browser. Flash is perhaps the most profound example of this. We moved back into a model with central storage and distributed processing.

For better or worse we oscillate between centralized processing and distributed processing. It’s the latest move for more processing to be done on the client side – without the need of specialized plugins that has driven the demand for JavaScript. However, I’m ahead of myself. We have to talk about what JavaScript even is.

What is JavaScript?

JavaScript is a scripting language which was designed to be used for lightweight applications and scripting. It was designed as an alternative to the strongly typed, compiled, and “heavy” Java language. That was more than 15 years ago. Since then it’s evolved into a tool that folks are using to solve enterprise class problems. As a language JavaScript is supported in nearly every browser on the planet. It’s a scripting language that is dynamic. That is JavaScript doesn’t rely on well-known types. In fact, at least one of the primitive types that we expect in languages – the integer – is completely missing from JavaScript.

Really, JavaScript is all about collections. A collection may be an “object” meaning that contains name-value pairs with the name being what people would consider to be a member of a class. The trick here is that the value portion of this equation can be anything – including a function. In this way, you can have a collection of name-value pairs that act like we expect a regular strongly typed object to behave. Arrays, the other type of collection, is simply a collection of nameless objects. The final thing to realize is that a value may be another object – and therefore you can have deeply nested hierarchies of objects.

JavaScript as just being collections may seem like an oversimplification – but it is a really helpful simplification because you realize that you can dynamically add and remove items from a collection at any time and thus it’s pretty different than a traditional typed language.

Why JavaScript is Good

There are some really great things about JavaScript. Because there isn’t a rigid type system you can do things in JavaScript that are difficult to do in a strongly typed language. You can slip an extra member into an object and use it deep inside the bowels of the code. You don’t have to refactor your methods between the top of the application all the way to the bottom to accommodate additional information – or even recompile. Conversely, without a definition of what you’re passing around it’s difficult to know if you’re passing in the right things. Let’s take a look at a few of the things that make JavaScript a useful language.

Cross Platform and Cross Browser

Perhaps the chief benefit of JavaScript is that it truly runs anywhere. Sure, Java — to which JavaScript has effectively no relation — claims that it runs everywhere but JavaScript actually does. It’s hard to find a web browser or operating system that doesn’t have a JavaScript interpreter. The interpreters have been getting better and better over the last 10-15 years. The result is that we have good interpreters of the language that are fast and mostly reliable.

The list of plugins that have tried to gain ubiquitous support and have failed is long and distinguished including Flash, Java, Silverlight, etc. Having achieved the goal of ubiquitous availability is an impressive accomplishment indeed. As a development manager it means that you’re going to be able to build once – well, almost.

The unfortunate problem is that much like the HTML standards, teams interpreted JavaScript slightly differently. They added access to browser specific features through the object model for the browser that would be supported on one browser or platform – and not on another. As a result there was time spent detecting what browser you were in to determine the browser specific way to approach the problem.

Libraries

The good news is that a set of JavaScript libraries began to emerge to allow for the developer to ignore the browser specific quirks. Chief among these libraries is jQuery. jQuery abstracted a way many of the differences and simplified many of the common – but complicated things that developers needed to do. jQuery handles everything from selecting items in the document object model (DOM) to making AJAX calls. All-in-all it’s the libraries like jQuery that dramatically improve the ability to develop with JavaScript.

With libraries for nearly everything that you might want to do, JavaScript’s functionality has been greatly enhanced. The problem is that you have to download and learn these libraries to be effective at them. It means gathering up bits and pieces from different sources and trying to patch them together into a meaningful quilt of code. Sometimes that works well and other times not so well.

Why JavaScript is Bad

If you ask most professional developers (as compared to a Hobbyist, see “What’s the Difference between a Hobbyist and a Professional“) about JavaScript and you’ll likely get non-positive responses. Most professional developers – that is those that get paid – don’t like JavaScript because it’s hard to make a craft of developing when you’re limited by your language. Most professional developers have come to expect a set of tools to help them develop more reliable code and to create the code faster in the first place. JavaScript isn’t bad for tying a few loose things together, however, developing enterprise scale applications is not what it was designed for.

Language and Library Richness

Unlike Java and .NET, JavaScript isn’t out of the box equipped with an array of libraries to call into. This means that you’re always on the search for a third party library that may be helpful to you. Simple things, like converting a play position from a video file and getting it to a standard text string expressing hours, minutes, seconds, and frame can take a hundred lines of code or so. (I just wrote this so this isn’t an arbitrary number.) The simple – like leading zeros for numbers – isn’t simple. Without the concept of an integer, integer division means grabbing the modulus of 1 and subtracting it from the number to truncate the number.

Professional developers who are used to ignoring some of the low-level functionality will be continuously frustrated by the need to develop their own library of routines to do the things that they’ve taken for granted in other languages. Sure I listed libraries above as a good thing for JavaScript –and it is. However, it’s filling the fundamental void which exists because the language doesn’t have a library of useful functions that it is built upon.

The problem is that libraries and operating environments clash with one another from time-to-time and because of this you find yourself working on getting code to work together. Consider a situation where two libraries require two different versions of jQuery, how do you get each of the libraries gets the jQuery that they need?

Static Typing

I remember writing code in C and C++ eons ago. I remember that my development environment was a glorified text editor. I remember needing to put print statements in my code to see what is going on – and that the idea of interactive debugging was a dream. Today because of strongly typed languages and better development environments we’ve learned to rely on our ability to interactively debug and more importantly we’ve learned to rely on the feature that Visual Studio calls Intellisense.

Hit a period and you’re going to get a list of things that you can do with that class. You don’t have to remember the method name or property name. You can browse from the list of items and simply select the item you need. From a cognitive load/instruction perspective this dramatically reduces the amount of mental processing required and makes it both easier and quicker to work on code. (You can look at my article for TechRepublic “Squeeze more out of the construction phase of application development” for a historical perspective on improving developer productivity.) In my book reviews for Efficiency in Learning and The Adult Learner, I speak more about the impact of cognitive load on learning – the same applies to development. The higher the load the lower the developer productivity.

A quick sidebar to realize that the primary constraint for developing code is the amount of workload the brain is doing. Developers are running mental simulations of how the code will run, they’re tweaking the code in their heads before they type it in. (See Sources of Power for more on mental models.) The in-head compiler doesn’t care about method names, it cares about operations and actions. Having to remember the exact method name is an overhead that distracts the developer from their primary job of making the logic work. In this way our new development environments have made development quicker – but that requires that the development environment know what options there are. When you’re dynamically putting in your own name/value pairs the environment may not know what is and isn’t expected.

The Intellisense functionality is really an extension of compile-time checking. That is a static analysis of the code to determine if there will be a problem. This isn’t possible in a true dynamic language. As a result not only do you not get intellisense, you don’t get the protection of the compiler telling you that you’re making a mistake. We know that the sooner that defects are discovered the less costly they are to resolve. If it’s resolved by Intellisense it’s really small. At compile time larger but still small. When you discover the problem in a developers debugging session it’s getting large enough to be called something other than small. If you have a problem that only occurs sometimes and is data/execution path dependent, it becomes expensive to locate. That means more development costs.

Code Structuring

Software development is still struggling as a profession to become professional. Exercises like the Software Engineering Body of Knowledge (SWEBOK) have attempted to catalog what we know about software development. As we attempt to dig our way out of the primordial soup of software development practices we’ve found that there are some things that are helpful. Like object oriented development mentioned above, we’ve discovered that there are techniques that simplify problems for our feeble human brains and therefore lead to better software. Some of the things we know aren’t effective in software development are the default behaviors in JavaScript.

A long time ago we learned that global variables are bad when used incorrectly. They led to spaghetti code that was difficult to debug and difficult to maintain. In JavaScript, by default, all variables are scoped at a global level. Without the concept of classes, or modules, we’re left with the default that everything has to be a global variable – or a local variable inside of a function. In order to mitigate the impact of this, we’ve built patterns to isolate the scope of a variable inside of JavaScript – but those patterns are working around the default behavior of the language.

Further, without a formal concept of namespaces, modules, or classes we find ourselves fighting to create structure where no structure is implied. It means that the default path for writing JavaScript is a bad path. Structuring code requires even more thought and focus because the language doesn’t lead to the behaviors of isolation that lead to structured code – and better developer productivity.

Clear Text Code

One of the other issues when talking about JavaScript is that the code is all clear-text. While this is good from a debugging perspective, it’s bad from an intellectual property perspective. It means that it’s trivially simple to look at someone else’s implementation of something in JavaScript and copy it. This reduces the competitive advantage for developing a solution to a problem and means that sensitive or proprietary techniques for processing aren’t great fits for JavaScript.

Having source code for someone else’s library may seem like a great thing in terms of helping developer productivity, however, from a corporate standpoint where they’re building their business on the competitive advantage of the code – through better operations or through the sales of licenses to use the code, the idea that your code is just hanging out there for all to see is very concerning – and potentially a barrier to development.

In my practice as a software developer it’s the standard that the client will own the intellectual property that I develop, in other words the code is theirs and they’re not going to share it. In some cases I’ve had clients who have allowed me to share this code with other clients – however, only in cases where the second client didn’t compete with the one who had paid for the work.

Development time

If you get to it the most precious resource in any development project is the developer or perhaps the architect. Developer productivity is absolutely essential to getting projects done – and on this score, JavaScript doesn’t do well. If you read the classics of software development like Peopleware, Code Complete, or The Mythical Man-Month, you’re realize that we’ve been fighting for developer productivity for a long time. I’ve not seen any formalized research on developer productivity in a strongly typed language – like C# or Java compared to JavaScript, however, in my informal conversations about it and in my experience with development teams, I find that developers are roughly 2 to 10 times faster at developing in a strongly typed language than in JavaScript. I’m willing to attribute some of this time to learning time, however, much of it has to do with the inability to build good tooling to support the development process.

Compositing

Many development scenarios today involve compositing. That is taking pieces from different places and getting them to work together. As mentioned above getting two libraries to exist in the same space can be challenging. If you load two different versions of the same library – jQuery for instance – how do you ensure that the most recent version is the one that is being used? Similarly, if the JavaScript is using targeting to find the elements – what happens when you include two sets of the same code on the same page – as might happen on a portal?

While these challenges are not insurmountable, they’re problems for which there aren’t great – generically applicable – answers.

Client Performance

The cliché answer given by a software developer about what’s wrong with code is “It works on my machine.” That’s probably true. However, it’s also equally likely to be true that the developer has a faster, better machine than most of the users who will be using what is developed. Developers get faster machines to be able to do their job. What happens when their JavaScript takes a reasonably long time on their computers? Eric Shupps was speaking about jQuery performance in his initial and follow up posts on the topic. Basically, Eric was sharing that jQuery walks the document object model – and thus is sensitive to larger pages and slower machines. By moving to JavaScript we can decentralize the problem and therefore make it easier to scale, however, we also make the problem sensitive to the remote machine.

If the JavaScript interpreter is broken then the page is broken. If the remote machine is underpowered then the site will appear to be slow. In most cases we’d prefer to have a great deal of control over our performance – and JavaScript is at the mercy of the users’ machine. The user-side problems can create a bad perception of our site.

Debugging

Perhaps the most frustrating thing about JavaScript has to be the debugging experience. It’s not quite back to the days of print statements to be able to see what’s going on. Development tools in FireFox – called FireBug – and in Internet Explorer – called Developer Tools – do allow you to interact with the JavaScript and see what is happening – but you do so disconnected from the authoring environment. Once you’ve determined the source of the problem you’ve got to go back into your development environment (Visual Studio?) and make the change then reload the page. This is certainly not the most developer friendly workflow – even if it can be used to accomplish the goal.

How JavaScript Makes Bad Developers

The title of this blog post may be slightly provocative. How can a language make someone a good or a bad developer? Well, the answer is really found in Kurt Lewin’s description of behavior being a function of person and environment. Many of us would probably agree that if someone behaves badly they would appear to be bad. And that’s really what I’m saying here. If someone does development behaviors (global variables, etc.) then we would label them as a bad developer – and since JavaScript makes this the default behavior, how could we do anything but say that JavaScript makes bad developers. But let’s explore this more deeply.

Making the Bad Easy and the Good Hard

If we accept that bad behavior is a function of the environment then why does it matter? Well, it matters because we’re establishing “norms” about how development should be done and establishing bad “norms” will create bad habits – and habits are hard to break. So if you’re used to neither having to think about how you’re structuring your data nor the scope of variables then even when in another language – you won’t get this the appropriate amount of thought.

I once had a developer who had grown up doing ASP development. In those days it was VBScript that was being used to build pages and just like JavaScript you could declare variables on-the-fly. That is you could just start using a token and it would become a variable. The problem with this – like with JavaScript today – is that you would make a typo (we’re back before Intellisense here) you would accidentally declare a variable and create problems for debugging. They added the ability to put ‘Option Explicit’ at the top of the files which would require that you explicitly declare variables. My developer wouldn’t do it – and would call me for help.

After numerous rounds of telling him to use ‘Option Explicit’ because I had found a simple typo in his code that created a problem I eventually had to tell him that if he called me over to debug his code again without option explicit at the top I’d have to fire him on the spot. This is not one of the brighter spots of my management career but I got his attention.

The discussion that followed was that he felt like it just slowed him down and that it was a polish thing – the thing that you did to make the code “right” at the end. The problem with this is that wasn’t a ceremony thing (Ceremony is a word used in agile to talk about activities that don’t generate results.) It was discipline to get him to think the right way about problems so that he could reduce defects. I wanted him to think about his variables and what they were being used for. I should say that JavaScript now has a similar concept “use strict”.

The point here is that we establish what the normal are – and pattern our thinking based on what we expect out of a language. So if we expect all variables to be dynamically declared at a global scope, we’ll design that way – despite the research that says this is a bad idea.

You’ll note that here I’m blaming the system not the developer. Nothing is accomplished by saying that they’re bad developers. Fundamentally I believe they have the potential to be good developers, if not encumbered by the language or system. I mentioned briefly in my review of Diffusion of Innovation that blaming people means you can’t solve the problem – blaming the system means you have something you can fix.

Bad Behaviors lead to Bad Habits

If we take the developer out of the language and put them into a language that requires strongly typed variables and offers them a local scope won’t they just change because of the environment change? Yes – and no. In the clear-cut cases, sure. They’ll default to the specific knowledge they have but in the less clear-cut cases those who are used to and accept global variables will leverage them more often.

Take for instance the case of a global logging class. It’s used just to log things out in case there’s an error. In 2002 I wrote an article “Focus on Functions” for Developer.com. In the article I talk about method (function) signatures. My strong bias is to pass in everything that is needed to function with the smallest objects possible and make the methods static. I do this to minimize unintended consequences. (It drives my testing friends nuts because static methods are hard to test.) So I’m more likely to include a logging class as a parameter to my methods than someone who’s comfortable with a global scope.

I purposefully provided a case where the right answer may be a globally scoped variable (or more appropriately property so it can self-initialize). However, what about passing around a SQL connection – or not. There’s greater testability when you can easily and consistently establish context.

The bad behavior that was learned through no fault of the developer will continue on into new languages simply because it has become the “norm” for how they develop.

An Argument against Being an Ostrich

I need to end with a call to continue to learn and grow. Whether JavaScript is the right answer for you – or not – exploring new techniques, tools, and approaches are a good thing. In another old article, “The Great Divide“, I talk about how people get stuck by not seeking out new and better ways of doing things. I’m not suggesting that you don’t do JavaScript development or you ignore the changes that are happening in our marketplace. Instead, I’m advocating that you go in eyes wide open – and that you look for ways to minimize the pain. Right now, as it comes to JavaScript the way I’m looking at this is through TypeScript.