To teach good programming, SIMPLIFY!
80% of the programmers I've associated with in the last 10 years rely on "cut and paste" operations. They look up how to do a task (on the internet) cut the code, modify the code for their environment, and think they're done. The most common languages where I see this is .Net and PHP, but there are lots of other examples.
Weinberg made a statement in one of his books that once a programming problem has been solved, it need never be solved again. (Just translated from one language implementation to another?) The idea of re-usable code and standard objects and patterns has led to code full of crap that nobody understands, yet they depend on it on a daily basis. (Some of the algorithms that Excel used for years were incorrect. For years competent programmers new that the floating point algorithms on 286, 386 and 486 math processors were not right, yet the everyday programmer would be ignorant of that fact.) The "reusable code", the "standard objects", the "libraries", the API's and almost everything else has made developers dependent on a multitude of code segments for which they have no understanding. Furthermore, the environment is so complex, that just finding the right code segment is enough to drive us crazy.
So, if you want to teach people to develop systems, teach them how to find and use the tools, but if you want to teach them to be competent programmers, go back to basics; work from the machine level up to higher-level tools.
A program is a set of instructions that work on data. Once you identify the data, it can be processed using only three methods; sequence, alternation and repetition. Teaching beginners how to transform data using these structures using logic gates and/or assembly language will build programming skills. Using decision tables, Warnier-Orr diagrams, or pseudocode to abstract the instructions from the language teaches them how to solve the problems, assembly language teaches them to implement the solution. After they can solve those problems, then they can build "objects" by writing code that contains it's own data.
After assembly I would have them advance to C or Pascal, and after they learn imperative programming languages they should go to something like LISP and Haskell.
Incidentally, sequence, alternation and repetition have thier own counterpart in Mathematical Logic, so, theoretically, it should be possible to prove the code correct (logically) and build correct code from provably correct components. And then, theoretically, it should be possible to generate provably correct programs from Hier level descriptions of the type of tasks that have to be performed on the data.
You get to decide at which point a "beginner" is no longer a beginner.