28 Mar 2012

Not Optionally-Typed, Gradually-Typed

Why was Dart so uniformly rejected by the development community; from both the dynamic-leaning and static-leaning developers?

What was it about Dart that was so distasteful? It has the "classic" object inheritance that essentially everyone but Javascript and Smalltalk developers cried for, while staying much terser than Java and keeping dynamic types available for those who prefer dynamic languages. If I could sum it in just one line, it would be:

int myBool = "Hello, World";

Don't you just want to punch me in the face with that line?

So, developers have long had to deal with terse or misleading variable names, but they've always been able to rely on the original declaration to either find out just what the variable actually is, or at least have a "var" warning sign that you're on your own with figuring out the code.

Dart simply makes it harder to debug source code without help from a debugger, purely by making types optional in their attempt to please both sides of the aisle.

Are static and dynamic languages truly irreconcilable? I don't think so. Programmers are programmers. They may have different styles of programming, but they're all after the goal of automating some task so it can be done far more rapidly than it could ever be done by hand.

So, what's the motive for choosing a static or dynamic language, when external requirements don't demand one or the other?

For those who prefer dynamic languages, I believe the motive is similar to the following:

I have been tasked with writing a new program. The general goal of the program has been defined, but how exactly it should be accomplished and how the user should use it has been left to me.

I don't want to use a static language that will force me to write lots of boilerplate code for a particular design that will probably be only half-right and have to be scrapped and rewritten a few times, drastically affecting code all over the program, when I can write in a dynamic language that will let me compose my functions and objects, shoehorn in new features as needed, and massage it into the actual design through a feedback loop of user testing and data structure analysis over time during development, vastly improving ROI for my client.

If/when a bottleneck in the code appears that requires static typing, I can rewrite that small portion in a static language when I have a well-defined idea of what it's inputs and outputs are, so I really can optimize it well, and don't have to rewrite it over and over.

While for those who prefer static languages, the motive is probably like the following:

I have been tasked with writing a new program. The general goal of the program has been defined, but how exactly it should be accomplished and how the user should use it has been left to me.

I don't want to use a dynamic language that will force me to write lots of boilerplate code to make sure my data input is the right type that will probably be only half-right and have to be rewritten a few times during the course of testing each component, when with a static language the compiler can automatically indicate issues with the composability of my functions and objects, add features on top of old without worry, and keep my code so much faster than dynamic code that I only need to worry about my data structures if and only if there is a demonstrated bottleneck demonstrated through a feedback loop of user testing and data structure analysis over time during development, vastly improving ROI for my client.

If/when the architecture being developed is deemed the bottleneck of the code that requires rewriting, I have a well-defined idea of what it should now look like, so I can really optimize it well, and I can reuse most of the already-typed code without rewriting it from a dynamic language.

The difference between the two types of developers, I think, is whether their approach is top-down or bottom-up (primarily) towards programs. Top-down developers don't want the minutia of the underlying hardware and data structures polluting their exploration of algorithms and architectures for the program, while bottom-up developers don't want the overall architecture distracting them from optimizing the algorithms and data structures that they will eventually build the architecture from.

But both sorts of feedback are important -- for the top-down developer, perhaps the underlying data structures necessary for an architecture can never be optimized into an efficient platform, even when deferred to a static language, and for the bottom-up developer, the data structures developed may never be composable into an efficient overall architecture.

Being able to explore in both directions helps tremendously, but few want to do it because that has traditionally meant "throwaway" programs written in another language to demonstrate feasibility -- code that can't really be re-used. "If I was just smart enough, I wouldn't have to write that other code, and could just write in my preferred language."

On top of that, clients can mistake the "throwaway" program demonstrated as the real McCoy, and demand development of the program continue until completion -- and then not only could the code be an awful kludge thrown together just to demonstrate some functionality, but it's also written in a language you're not as comfortable in.

A single language that could allow both top-down and bottom-up exploration and development. It would, by most definitions, be considered a dynamic language, but it wouldn't be a "pure" dynamic language like TCL, but some sort of hybrid that allows you to mark certain variables as a fixed type, and restrict what can be done with certain object definitions, like a static language.

Then dynamic variables and objects can be used for top-down exploration of the architecture, while "well-defined" parts of the code can be "solidified" into static types to improve performance and future maintainability, gradually typing the source code until completed.

Dart misses that point, but what out there "gets" it? HaXe is one example of a language that can bridge static and dynamic languages; it can compile to Javascript, Actionscript, Java and C++. (It's too Java-like for my tastes, though, and the fact that it's a language-to-language translation means that you'll always have to deal with unfamiliar error messages for whatever target you're compiling to.)

However, one language particularly important to the Dart developers seems to get it: Javascript.

The latest version of Javascript has moved towards a "gradual typing" system like I've described.

  • There are now typed arrays for storing guaranteed integer and double values that can be iterated over and operated on far faster than normal types.
  • There are buffer objects, references to raw binary data, that can be passed through Javascript to various APIs such as WebSockets, WebGL, and so on.
  • You can now explicitly define objects and "freeze" their definition so the engine can optimize the object type in memory and further improve performance.
  • A custom taken from compilers of old to initialize variables on declaration can be used as hints to the JS engine about the type of the variable to speed JIT optimizations.

And that's just from the most recent version (ECMAScript 5) of Javascript. Future enhancements currently in debate would bring in true "classic" inheritance and actual explicit typing (with errors on mismatch).

As pressure is applied to Javascript to do everything traditionally falling upon the shoulders of static languages, it appears that a hard diamond may be forged from the soft coal.

Dart was poorly received because their attempt to please both camps was done by compromising the workflow of both. The future of Javascript may actually succeed by enhancing the workflows of both, instead.

19 Dec 2011

node-localize, a Node.js localization library - moving beyond gettext emulation

node-localize is a library that I've been working on for some time, and now it has enough features that I think it could be useful for others.

It's a localization library for Node.js, inspired by GNU gettext, but it does not restrict itself to the C-centric API of gettext, like a couple others do. (That in itself speaks about adapting gettext to Node.js. Should you use *.po files or *.mo fles? Does compiling the translations into a binary format designed for fast lookups make any sense for Node.js? Are the gains in space important?)

But this isn't about gettext, this is about node-localize, a library designed to work well with Node.js.

So, what advantages do we get over the gettext clones?

First, the library is an object in which multiple instantiations are possible, so you can handle your translations as you want; spawn a singleton object if you only need a global translation method, or spawn multiple if you want segmentation of the translations, such as static translations versus dynamically generated (user submitted?) translations.

Translations are stored in a simple JSON object and can be constructed in your application and loaded, or can be stored in a JSON file whose path is passed to the Localize object on instantiation, so they fit better in your workflow and can be manipulated programmatically with ease.

Translations can also insert variables into desired locations in the translated text with a syntax mixing jQuery and sprintf, which goes beyond gettext and allows the order of the inserted variables to differ between languages (particularly good for translating text between Asian and European languages).

Large translations can be stored in a special translations directory and referenced by a simple variable name (the variable name matching the file name of the default translation, minus the file extension).

node-localize also incorporates (and modifies, so it's forked) the node-dateformat library so you can easily define date format types, translate them between languages, and use them on dates in your code.

The xlocalize command works in a way similar to xgettext, parsing source code and automatically generating translation templates for you (and correctly updating already generated translation files without overwriting your translations).

Planned features include pluralization support (the only feature of gettext not yet available in node-localize), numeral and currency formatting differences between languages, optional country code support (that falls back to the language baseline if not found), and support for running inside of browsers (possibly to include an exporting functionality to port desired translations to the browser, as well.

The entire API is viewable on the github repository, and you can install it with a simple:

npm install localize
16 Dec 2011

UCF VPN in Linux (KDE)

I figured I'd make a quick blog post about how to connect to the University of Central Florida's VPN in Linux (kubuntu 11.10, to be precise) for anyone who has a need to, since I had to figure this out. UCF recently changed their VPN software over to Cisco AnyConnect VPN, which works in Windows/Mac, but not Linux -- so these instructions should work for at least the next few years.

First, make sure you have the Cisco VPN provider for Linux, vpnc, installed.

sudo apt-get install vpnc network-manager-vpnc

Next, make sure you know your NID username. You can look it up with this form.

Then, make sure you know your NID password. You can reset it with this form. (You need to know your PID for this one, which you can look up with this form.)

Now, click on your networking system tray icon and click the "Manage Connections..." button. Then go to the VPN tab and click the "Add..." button and choose "VPNC".

Fill out all of the fields as they are in the screenshot below. Note that if you are a student and not faculty or staff, you need to set the group to "ucfstudent" and the group password to "knights", instead.

Ucf_vpn

And that should give you access to everything as if you were in campus.

12 Oct 2011

What do we want from a web programming language, anyway?

There's been a lot of angst about Google Dart amongst programmers since it was revealed to the public a few days ago.

Dart positions itself as a more optimizable Javascript, but has an atrocious Dart-to-Javascript compiler. It is true that this compiler will probably undergo its own optimizations, and that Dart's added capabilities implies at least some overhead when run in Javascript, but this is true of essentially any program automatically translated from one language into another.

There are more serious complaints about the language, however, such as a type system that doesn't really work providing the worst of both dynamic and static languages, in my personal opinion, since the JIT cannot be sure a particular variable will ever be assigned the wrong type of data, but the programmer still has to annotate the data they're working with.

There are some minor complaints with Dart, as well, such as hardwiring the Java object factory pattern into a language that doesn't really explicitly need it, but this is roughly equivalent to Javascript's redundant new keyword since you can modify the inheritance structure of an object directly and have the constructor function return that object.

The concurrency model, Isolate, seems heavily-influenced by Web Workers, as well, by making it so the parallel code is highly isolated and can only message pass back-and-forth. I find the W16 experiment using Software Transactional Memory with parallel event loops much more interesting, because for most cases it will make fairly linear event-driven code scalable (only pathological cases where the code is dependent upon the event dispatch order from unconnected events would this fail to produce a speed-up, but I believe most Javascript code is not written in that way).

Clearly, Dart-as-a-language, not just Dart-as-a-language-cross-compiled-into-Javascript, has warts, but are they more or less than Javascript?

Well, doesn't that depend on what we want to use it for? The major complaints against Javascript are:

  • Poor optimization target because any variable can contain anything.
  • Inability to automatically eliminate dead code because functions can be called dynamically.
  • Hard to organize because of no built-in way to require libraries. (The C language doesn't have any defined way to do so either, but the C pre-processor was developed to handle this and is essentially standard; web browsers never exposed a good way to do this.)
  • Language will do nothing to stop you doing something stupid, and won't crash when something stupid is done (such as trying to divide a boolean by a string).
But look at these complaints from another light:
  • Rapid prototyping because a variable can contain anything.
  • Dynamic function calls greatly reduce the code needed to allow customizable behavior of the code ("native-looking" RPC calls by assigning an RPC anonymous function to each desired function name defined from an array)
  • No pre-processor magic causing code to do non-obvious things.
  • A bug in your javascript is less costly because it won't cause the website to crash.
The reality is that there's a whole spectrum of things that we want from a programming language, which is why we have more than one language out there! Restrictions of certain sorts can make reasoning about a program in certain ways simpler, and can reduce code size for particular kinds of tasks.

As others have argued, perhaps that means we should just standardize on a bytecode to run on a virtual machine inside of the browser and let any language that can compile into that bytecode be used by the developers. There are advantages here, as certain languages can put in restrictions they deem important for understanding, such as the near-total elimination of function side-effects from Haskell, or the elimination of blocking calls from the API in Node.js. But I feel that breaks the spirit of the web where the code run on the client is fully accessible and auditable, letting users learn from others and become full participants, as well.

Further, allowing multiple languages in the browser will lead to more severe fragmentation in capabilities and probably a worsening of the functionality of the web when the lowest-common-denominator is reduced to whatever subset of the language you chose is supported across browsers.

So, if we're to replace Javascript with Dart, what changes should we make to Dart's specification to better handle both extremes of strongly and weakly enforced structure, ideally allowing a transition from weak-to-strong as the prototyping is wrapping up and the functionality is becoming more rigidly defined? My opinion:

  1. Get rid of optional typing, and replace it with both strong and weak typing. If the typing was strictly enforced, the JIT could quickly generate optimal assembly to perform the desired actions upon that variable, but then any non-standard type, such as boolean + null, would have to have a full-blown Dart class and object construction, which is probably slower than what V8 does with a variable that, say, starts out null and is then defined as a boolean.

    If Dart allowed a special "var" type that accepts anything just like Javascript, you could start out with everything being "var" and then go back and redefine it to "int" or "float" based on how you ended up actually using it. A Dart IDE could then show the developer which type the variable is when they mouse-over the variable name, and a Dart linter could warn about var variables present. If you explicitly want something where the type of variable can change (IE, you want to do duck typing on a set of objects rather than use generics) a convention in the linter to ignore any variable starting with "#" could be used, similarly to how "_" is used to denote private components of a library, so portions of the code that truly ought to be dynamic are easy to identify.

  2. Unify the function types into one. One thing Javascript gets right is that functions are first-class and can be passed around at will. The distinction between anonymous functions with an implicit return and named functions with explicit return will only confuse things.
  3. Allow dynamic function calling, but not by default. This capability, Object["funcName"](), allows for very concise code in Javascript, but makes dead-code optimization impossible and is more prone to screw-ups by a mediocre developer, so it shouldn't be on by default, but should be allowed for a prototyping stage.

    I propose a syntax such as allow("dynamicFn"); that can only be called from the main() function of the Dart source. The parameters passed into it can only allow more permissive code, not restrict it, such that libraries will be pushed towards the highest-performing, widest-possible-usage scenario (as they would cause errors if included by code that does not allow these things). I think this is a better idea than Javascripts' "use strict";

  4. Allow compilation of Dart to shorter, unsafe Javascript, if desired. Because Dart will exist as a compilation into Javascript for some time, like CoffeeScript, an option to forgo the type safety of Dart for faster real-world performance should be allowed, since the Dart compiler should be catching these type issues ahead of time, anyways, providing most of the advantage over Javascript immediately, while the developer would attempt to do this sort of "straight port" themselves if the performance/bandwidth difference truly matters for their application.

    A less obfuscated compilation into Javascript would also allow the mixing of Dart and Javascript code in the same codebase, rather than Javascript being only a target. This would help increase Dart adoption since the jump into the language would not be as steep.

That's my opinion on the matter. I've tried to be fair and as unbiased as I can towards Dart, with my suggestions focused primarily at getting a better middle ground between the easy prototyping of Javascript and the more solid assurances of Java, without affecting the verbosity of Dart (which I think is already a nice middle ground).
20 Aug 2011

Better organize your Express web application with express-route-util

So, you've chosen to use Node.js for your next project, perhaps because of the various benchmarks showing the V8 Javascript engine getting a dynamically-typed language to perform as well as statically-typed languages, and Node.js's HTTP performance surpassing Apache's.

But, your web application isn't doing anything special with the HTTP protocol, and you don't want to handle that manually, so you chose to use the Express framework to take care of that.

So you start out using Express similarly to MVC frameworks like Ruby on Rails or Django, but there are a few things that are missing. Some of that seems fine; there's no explicitly-chosen Model, but that means you can choose your own, like Mongoose, FastLegS, or RDX. Express also supports a few different view template engines such as jQuery Templates, Jade, and EJS.

And controllers seem to be the easiest portion of Express, just write:

app.get("/path/to/page", function(request, response) {
    response.render("myView", {hello: "world"});
}

But some problems arise. The controller functions grow huge with anonymous callbacks as you interact with your model, there's copy-pasted code in several of them as you include session handling for login verification, and your source file is huge as these controllers are all in the same scope as the express library.

Express has solutions to all of these problems. The method calls to register your controller can actually accept any number of functions, any one of which can choose to render an output for the user, or "pass the buck" along to the next controller in the list (by calling "next"). So common functionality can be abstracted, like so:

function isLoggedIn(request, response, next) {
    if(request.session.loggedIn) {
        next();
    } else {
        response.redirect("/loginRequired");
    }
}

app.get("/path/to/page", isLoggedIn, function(request, response) {
    response.render("myView", {hello: "world"});
}

Now your functions have been reduced in size, do just one thing each (perhaps piggybacking model data on the request object) and it feels better. But there's still a few problems: it's all in the same source file so its hard to find exactly what you're looking for, it's hard to get a bigger picture about what it is your web application is doing, and it's hard to make a change to any controller's URL because references to it are strewn about everywhere.

The express-route-util was written with this in mind. Unlike Ruby on Rails or express-resource which solve this problem by restricting the meaning of controllers to directly correspond to a URL (and changing a URL requires changing the controller name), and restricts what controller names are valid, this utility continues to expose the full functionality of the Express engine while helping you organize your source code and URLs.

The controllers are defined to use a directory hierarchy like so:

controllers/
  controllers.js
  social/
    controllers.js
    helper_lib.js

The utility only imports the export objects of the controllers.js files, so other code not to be used as a controller can reside within separate library files for even better source organization.

The exported functions are referenced relative to their location in the directory hierarchy, using a dot-notation just like a Javascript object hierarchy, so exported functions from the controllers.js in the root of the directory structure are at the root of the hierarchy, such as "index", "login", and "logout", while those in the social directory's controller.js are referenced by "social.viewProfile", etc.

The URL hierarchy is defined by the user in a simple JSON tree such as:

{
    '/': 'index',
    'get,post/login': 'login',
    'post/logout': ['common.requireLogin', 'logout'],
    '/social' : {
        '/': 'social.index',
        '/profile': {
            '/:username': 'social.viewProfile',
            'get,post/edit': ['common.requireLogin', 'social.editProfile']
        }
    }
}

The basic syntax for the keys is [HTTP methods]/url/path/:variable, where [HTTP methods] is an optional, case-insenstive comma-separated list of HTTP methods, and the rest of the key is a standard Express URL.

The value for a key can be a string indicating the particular controller to use, an array of strings indicating the controllers to use and the order to use them, or an object, following these same patterns, allowing a sub-sectioning of URLs.

When a key is holding an object, any HTTP methods you indicate are ignored, and the given URL is prepended to the URLs of all keys within the object, so the social.editProfile controller would have a URL of /social/profile/edit/ and the URL will function for both GET and POST requests.

The intended usage of express-route-util is as follows:

var router = require("express-route-util");

router.setDefaultMethod("get");
router.registerRoutes(app, urlJSON, "/path/to/controllers/");
var href = router.getControllerUrl("social.viewProfile", { username: "dellis" });

Where app is the Express application object, urlJSON is the JSON object relating paths to controllers, and "/path/to/controllers/" is the location of the controllers for the project (can be a relative URL).

A "good" set of defaults for registerRoutes is:

router.registerRoutes(app, JSON.parse(fs.readFileSync("./paths.json")), "./controllers");

This way, you have an overall hierarchy similar to:

app/
    controllers/
        controllers.js
        ...
    public/
        ...
    views/
        ...
    app.js
    paths.json

And you'll probably want to attach the router to the global object, as the getControllerUrl is most useful to the controllers to generate the URLs to pass to the views.

To install using NPM, type:

npm install express-route-util

That's all there is to it!

20 Jun 2011

Information Resources

I think there is a world market for maybe five computers.
- Apocryphal quote attributed to Thomas J. Watson

So why are these computers such a big thing, anyways?

This tongue-in-cheek thought came to my mind while waiting for bash script I wrote to process a file literally over 8 million lines long. That could easily lead to a talk on program optimization and using the right tool for the job, but that's been done to death, hasn't it? Now, while I was impatiently waiting for my script to complete its job, I was suddenly bemused that I was upset this was taking any longer than 5 minutes in the first place. Who would expect a human to complete this task on any timescale shorter than weeks?

So I started to feel that sense of wonder at the little notebook sitting in front of me, when I then saw my even smaller cell phone, and the wondrous things it can do, then I thought of the GPS and the wondrous things it does, television and what it has done, and the internet which is the lifeline of all of these now. Looking back throughout history, thinking of the similar things they supplanted, I came to a conclusion:

A GPS is like binoculars,
A computer is like a pen and paper,
And the internet is like a horse.

What do I mean by this? First, let's ask ourselves a fundamental question: what is information? That's actually a very hard question to answer without using a synonym for information. Go ahead, try to think of something that doesn't include "data", "knowledge", or other words. :)

Information is a property about something that can be observed.

We observe things constantly, our eyes inform us of our surroundings, our ears tell us about activity around us, our nose directs us to nutrition and away from poison, our tongue determines what we are consuming to help us decide if we want more, and our skin reacts to objects it touches.

Binoculars or a GPS are information observers -- they help us acquire information, such as whether that dust cloud on the horizon is merely a sandstorm or an invading army, or our exact location on the Earth within a few meters, updated every second.

When we acquire information from our senses, our brain attempts to make sense of it.

What was that shadowy movement in the corner of my eyes? That rustling sound from the same direction? That damp smell in the air and dryness in my mouth? Oh, now I know, I can feel a dog latching onto my back! From the sound of its growling and the clawing into my skin, I know roughly where it is, so I can stab my spear backwards against it. Now I can look closely at the dog and can see that it has a collar, and must have been trained by a neighboring tribe! I'm losing too much blood from my wounds. I want to warn my own tribe even if I can't make it back on my own, so I take out a piece of parchment and a quill, write what has happened, tie the parchment to my spear, and throw my spear back into the clearing between my village and the woods; somebody will hopefully see it.

The quill and parchment, a computer, and so on are information processors -- they help us process and encode information we have observed into a more succinct piece of relevant information. In this example, the entire sensory experience of the hunter was processed in his brain, then transcribed with the quill and parchment into a reduced, encoded form (language) that can be read by his fellows. Now, one may thing of the parchment as just another piece of information -- and by itself that is true -- but when coupled with the quill, it provided the hunter with the tool to process that large body of information from his five senses into something his compatriots can understand.

By this point, it should be clear what the horse and internet have in common -- they are information transporters -- they help us move information from one place to another, but cannot do any processing of that information on their own.

When we look at the technological advancements of the last, oh, 200 years, they apply to essentially one of two domains: information and material. Technological advancements involving materials, material processors, material transporters, and material observers has been aimed at sating our needs and wants. Our technological advancements involving information have been primarily involving in meeting these material needs and wants.

But for the past 60 to 70 years, our material desires have been mostly sated. Food, clothing, shelter are essentially taken care of in the developed world. But our capitalist system demands companies to continue fighting for a greater share in meeting these desires. Classical economic theory considers the participants as rational actors who make transactions in their own best interest. To determine "best interest" perfectly, one must know the entire economic situation of everyone, everywhere, at every moment, which essentially an impossibility. But to know more and more about what is occurring around the world than others can give a special advantage to businesses, and so they pursue information fervently.

Now information itself has become a powerful source of income in economics. Research and Development funds are spent on the pursuit of new information (by synthesizing observations into a compact piece of information that can be used more easily -- Science), computers to process information are a massive industry, the telecommunications industry to move information around the world is a massive industry, GPSs, accelerometers, gyroscopes, and other sensors have become commonplace in cellphones to help people observe information immediately from their cellphone, even the gathering and dissemination of pure information has become big business (Google, Facebook, Posterous...). Information itself has developed a very large value in our economy (which can explain the attitudes of those in favor of strict IP regulation).

There is one other reason why information has been the driving force of technological development this past half a century -- having mostly sated our needs, we can attempt to sate our wants; to entertain ourselves. As we interact with the world through our sensory-gathering organs, our sources of entertainment can be boiled down to information presented to us. The developments of radio and audio records, television and video recordings, and video games have begun fulfilling our entertainment desires through our senses of sight and sound.

The end-game scenario is obviously the Holodeck, where all senses can be provided for. We aren't there yet, and I can't begin to imagine what exactly humanity would do if we achieve a perfect substitution of reality for whatever entertainment a person sees fit to pursue.

Our pursuit of knowledge, however, has also taken us to the edge of the solar system itself, and our exponentially increasing population will eventually hit a hard limit on what is sustainable for planet Earth. Perhaps our need for materials will come once again and cause us to rise up and go exploring reality once more.

But we'll always need to be informed of our surroundings as we explore...

15 Jun 2011

Opinions and Engineering

I'd like to start out this blog -- my first ever -- on a topic that affects everyone ever involved in a real engineering project, and yet doesn't seem to be addressed much.

Engineers are people, and people have opinions. What do you do when two engineers have conflicting opinions? This article concludes that there is no ethical problem with conflicting opinions on an engineering problem. But how to reconcile the differences in opinion? If you need to make an engineering or business decision, this must occur eventually, or the project will go over time and over budget. But having no systematic way of determining how to reconcile the opinions removes the engineering from the solution into something superstitious: "We're taking this path because I trust this guy, and I trust this guy because he has my trust."

First we need to figure out why there are differences of opinion. (Essentially, we're answering the old journalist's question list of "Who? What? Where? When? Why? How?" only "who, what, where, and when" are usually very easy and implicit to this discussion.)

The first source to check is a difference in experience. This is not seniority; experience is a multi-dimensional thing that grows in its total volume (area? size? what's the right word for an n-dimensional space?) over time, but not uniformly. A difference in opinion may be coming from the differing engineering backgrounds of the two (or more) engineers. Having the engineers discuss and debate the merits of the different proposals is an obvious solution, but a recent proposal that we're wired to try to win debates rather than find the objective truth would explain why this can often devolve into egotistical pissing matches.

Perhaps putting this solution on its head can help: have the engineers argue for each other's opinions. If the engineers reason out the valid points of the other's suggestion, it may not seem so anathema afterwards (they may come to feel as though they "own" some of the points as well -- especially if they find a point in favor that the originator did not). Then actual reconciliation of the opinions could occur, throwing out the ego-game.

But the difference of opinion produced by a difference in experience may not produce a situation where one engineering opinion is better than another. If the problem space of the particular problem has multiple peaks, two engineers starting at different points in the problem space (starting out with different focuses of experience) may come to wildly different, but equally valid solutions.

In this situation, any reconciliation could produce a result that is worse than if either opinion was followed unaltered.

There is also the possibility that one of the opinions is actually wrong because of a lack of relevant experience; a reconciliation of opinions would only dilute the more correct opinion, in that case. It would be hoped that the procedure above would cause the less-experienced engineer to about-face on his original proposal, but he is still human and may not do so.

Peer-review can help here, but also runs the risk of debate manipulation, e.g., My solution is similar to previous solutions we've used and is familiar to you, his is different and unfamiliar!

Ideally, a group of peers with drastically different backgrounds and no investment in either solution would be available to judge the opinions. Scientific journals attempt to produce this sort of environment, but are of course limited by the finite number of researchers, the researchers' finite time to read published papers, and the built-in egotistical drive to be the one who produces the game-changing solution. These published, public reviews also take quite some time to be resolved, often measured in years, and therefore are not appropriate for all issues.

There is also the limitation that such a review is public to those allowed to review, so any problem treated as proprietary by those attempting to solve it have a drastically reduced audience for review.

One possible solution to the deadlock produced by the last two scenarios where reconciliation is undesirable is to analyze the opinions through cost-benefit analysis, specifically, what is the cost of attempting each proposed solution, and what is the expected benefit. Cost, in this case, can refer to money, time, and resources (meaning a particular resource that is finite and cannot be bought or possibly even remade, such as a prototype). How they are weighed is another engineering problem, itself, but any large engineering group most likely already has these at least roughly weighed out.

In this scenario, the "cheapest" solution would be tried first. For the situation where both opinions are "right" but different, the problem is immediately solved and the group can move forward along the cheaper path (which could therefore be seen as the better path, in some sense). In the scenario where one is right and one is wrong, if the one who was right was more expensive, the group has thus disproved the original proposal by the failure of the attempt, and can move forward on the correct, more expensive path. If the one who was right was the cheaper, then the group has not wasted an inordinate level of resources on the problem.

That is, assume that both projects total to a cost of 1. The cheaper project costs 0.3, and the more expensive costs 0.7. If this strategy is applied consistently on good/bad engineering opinions, and assuming that the distribution of good and bad opinions between cheaper projects and more expensive projects is 50/50, always attempting the cheaper solution would cost on average 0.65 versus 0.85 for always choosing the more expensive option. Some overhead versus the ideal 0.5, but better than the alternative of always choosing the more expensive option and most likely better than trying to be "lucky".

An example: In this research paper I wrote, I am quite proud of the fact that all of the equations were written prior to any measurements being performed, with the resulting measurement data exactly matching my predicted results. However, I was challenged (by one of the co-authors) that I made too many assumptions in the derivation (I did make assumptions, and spelled them out explicitly, along with a brief explanation as to why I believed it was justified), that some of the assumptions should be removed, and that this be tested first.

I was able to argue that this would require more time for me to re-derive the equations, and would require more measurements, in which the very measurements needed for the equations I proposed would be just a subset for the more generic equations. I also stated that I could complete all of the needed measurements in a week, so I was given permission to use the material and perform the measurements, and sure enough, I was justified in the assumptions I made.

In this case, the total cost of both proposed measurements is 1, while my measurements were 0.3 and his were 1 (as mine were a complete subset of his -- my proposed solution would produce measurements that did not match the predicted behavior if my assumptions were not correct).

Opinions in Engineering happen, and they can cause an otherwise ordered group to turn raucous. One or both parties in the argument over engineering opinions may be right. The solution to dealing with opinions in engineering can itself be treated as an engineering process (yes, that can recurse infinitely), first attempting to manipulate human nature to nullify its impact as much as possible, and then attempting to mitigate the cost involved with choosing poorly. This process has been demonstrated as an empirically better process (the 0.65 average cost versus the 0.85 average cost), and anecdotally, too! ;)

David

David Ellis's Space

I'm David Ellis from Orlando, FL currently living in Sunnyvale, CA. I have a Bachelor's in Computer Engineering, Master's in Electrical Engineering, and pursuing a PhD in Electrical Engineering. Working as a Web Developer at my university. Married with one young boy.

My interests are all over the place, ranging from reliability modeling, physical modeling, circuit design, circuit board design, low-level programming, high-level programming, scripting, system architecting. Essentially the entire "stack" from a single transistor's behavior and reliability up to whole information systems design and reliability, and I'll blog about whatever catches my fancy, though I'm sure to have "streaks" of focus on one topic or another.