Fri Apr 20, 2012 8:55 am
Deciding what software development language is to be used before considering any of the project requirements is sheer folly. If you only have a hammer, everything starts looking very much like a nail, whether or not it can withstand hammering. That wastes precious resources (how much is your time worth?). Not every language is created equal, and even different implementations of the same language can vary widely (the lone exception being Ada – there is one, and only one, _validated_ and _verified_ version, per release).
There are versions of BASIC that can be compiled, others that allow very structured software to be developed, and yet others that encourage the production of infamous "spaghetti code". Object-oriented software cannot be developed in all languages, at least not by the textbook definition of OO, where data abstraction, encapsulation, messaging, modularity, polymorphism, and inheritance are _required_. Some of these can be simulated through tortuous incomprehensible mangling of data structures and flow-of-control mechanisms, but, others are just simply impossible if not designed into the implementation of the language.
The sooner people forget the words "programming" and "programmer", the better off we all will be. We should be teaching software development or, in the ideal, software engineering. It has been well-documented over the last half-dozen decades that maintainable, extensible, minimal-error software involves a _lot_ more than implementation (aka "programming", but, even that isn`t an equivalence). In fact, the average software developer spends only about 15% of their time on a project actually implementing the software (aka "programming", but, again, that isn`t an equivalence, either).
Before you even type a single character of source code, you`d damned well better know what the hell it is you`re supposed to be developing. The number one cause of software project failure is not understanding requirements in all their variety (user experience, performance, accuracy, reliability, extensibility, etc.). Getting the wrong answer at the speed of light in binary isn`t a very useful way to spend your time, just as it is using any language.
Once you know what the requirements really are (and prototyping may be necessary to really understand them, but, only after _some_ level of requirements extraction from customers and users), you still don`t get to put fingers to keys. You really need to design anything much more complex than a Hello World! program. Did you know that the combinatorics for any software more than about a few dozen lines in length can result in programs with more bugs than can be discovered in a person`s lifetime through testing (which includes debugging – we`ll get to that in a minute).
This is why design is critical – it`s not some egg-headed exercise in mental gymnastics, especially if more than one person is going to work on the project, the people working on a project haven`t done so together extensively before, or only one person is involved, but, they will need to revisit the software only occasionally over extended periods of time (ask any software developer how often they`ve returned to source code they wrote, and didn`t have the foggiest idea about what they had done or, more importantly, why). Design also includes determining how much code you can reuse that was developed elsewhere, by yourself or others, often in the form of libraries, but there are other choices.
This should have an immense influence over your implementation language choice – if there are already libraries that do most/all of what needs to be done, _use_them_!!! Even if you`ve never used the language they may be implemented before, you are much more likely to be successful using code that`s already functional and (mostly) debugged (assuming it`s coming from a reputable source). Learning languages you`re not familiar with will only broaden your experience and professionalism. There are great tutorials and even world-class courses available on-line for free (e.g., MIT, UC Berkeley, Stanford University, Carnegie-Mellon University, etc., and perhaps these now exist at Cambridge, and other top-tier universities around the world).
Once you`ve got the requirements and design reasonably defined, then you can start implementation, but, again, there are more and less effective ways to do that. If you don`t write the comments first, you are asking for trouble – they are how you, or especially others, will be able to come back later and understand what you did and why. They should flow directly from the design, and reflect every single requirement that the customer/users voiced that you agreed to implement (customers/users aren`t always right, but, their wallets are). In fact, I always cite requirements by number/name, via URL, if possible, within my comments. Then, write just the definitions of all of the functions, procedures, subroutines, or whatever calling structure the program will have. Do _not_ attempt to fill them in with any internal data structures or executable statements other than calling and return types of statements.
You want to be able to run the program with nothing but calls to these functional interfaces where you simply enter them and then immediately return without actually doing anything. This is absolutely critical, and virtually no one but the most expert software engineers does it. If you can`t get the flow of your program correct purely at the interface level, you have already doomed yourself to debugging hell and probably project failure, in terms of cost and schedule.
Only after all of the interfaces have been correctly implemented and tested for proper flow should you next add the internal data structures that will be used within each function/procedure/subroutine/etc. Then, run the program again, without any other implementation added, yet. Fix all of the inevitable syntactic errors and only then should you proceed to the executable statements that actually do the heavy lifting in software. Again, start from the top/outside and work downward/inward as you did with the interfaces.
When you implement a conditional or looping block, don`t include any of the internal statements to be executed within, just immediately terminate the block and then test it. If you`re using a source code editor that features auto-completion of blocks (e.g., when you type a left curly brace, it immediately includes the matching right curly brace to the right of the cursor), congratulations. However, if you`re not, you _must_, I repeat, _must_ immediately complete the block, test the code, and then go back and fill in the remaining statements when you`ve killed the bugs you _will_ introduce. Keep doing this block-wise expansion and testing until, eventually, all of your code is written. This technique alone will save a considerable amount of your lifetime wasted in unnecessary debugging, no matter what language you`re using.
There are specific variations necessitated by certain languages and implementations of them (e.g., languages that allow function/procedure/subroutine/etc., interfaces to be defined separate from their implementations), but, it shouldn`t take much imagination to figure out how to adapt the above strategy I described. If you do all of this correctly, you will spend much less time debugging problems that may be nearly impossible to find by anyone, much less yourself (forest and the trees problem). In fact, the only bugs you should find should be design mistakes where you and/or the customers/users didn`t correctly identify requirements. There are various strategies where you can start developing the user interface elements and have the users test them to validate they make sense before you even design the back-end software that`s going to be doing the actual processing (e.g., a database, web CGI, or other services). Again, getting the wrong answer at the speed of light is not a good way to expend valuable resources. Figure out what the software is supposed to do before solving anything else.
Real engineers (software, or otherwise) will tell you that there are three fundamental, conflicting goals that need to be resolved in planning and executing any project: time-to-develop, cost, and quality – but, you only get to pick any two of these to be satisfied. You can achieve high quality at low cost, but, it`s gonna take time that no one ever seems to be able to comprehend. You can expend a lot of resources producing something very quickly, but, it`s gonna be crap by almost every measure. You can buy quality over a short period of time, but, you are _going_ to pay through the nose, and pretty much every other orifice from which money can be extracted, in the most painful ways, of course.
If I`ve saved anyone even five minutes of debugging hell, this will have been worth writing. In any case, enjoy!
The best things in life aren't things ... but, a Pi comes pretty darned close!

"Education is not the filling of a pail, but the lighting of a fire." -- W.B. Yeats
In theory, theory & practice are the same - in practice, they aren't!!!