Interesting articles include:
Thinking in Types, and Good Design
Students who become accustomed to programming in languages like Python and Ruby often become accustomed to using untyped lists, arrays, hashes, and tuples as their go-to collections. They are oh, so, handy, often the quickest route to a program that works on the small examples at hand. But those very handy data structures promote sloppy design, or at least enable it; they make it easy not to see very basic objects living in the code.Code Duplication as a Hint to Think Differently
Who needs a Game class when a Python list or Ruby array works out of the box? I'll tell you: you do, as soon as you try to almost anything else in your program. Otherwise, you begin working around the generality of the list or array, writing code to handle special cases really aren't special cases at all. They are simply unbundled objects running wild in the program.
Good design is good design. Most of the features of a good design transcend any particular programming style or language.
When defining a program to process an inductively-defined data type, the structure of the program should follow the structure of the data.
This guideline helps many programmers begin to write recursive programs in a functional style, rather than an imperative style.
Take Small Steps
If a CS major learns only one habit of professional practice in four years, it should be:
Take small steps.A corollary:
If things aren't working, take smaller steps.I once heard Kent Beck say something similar, in the context of TDD and XP. When my colleague Mark Jacobson works with students who are struggling, he uses a similar mantra: Solve a simpler problem. As Dr. Nick notes, students and professionals alike should scale the step size according to their level of knowledge or their confidence about the problem.
Software Design is a Bet on a Particular Future
This truth is expressed nicely by Reginald Braithwaite:
Software design is the act of making bets about the future. A well-designed program is a bet on what will change in the future, and what will not change. And a well-designed program communicates the nature of that bet by being relatively flexible about things that the designers think are most likely to change, and being relatively inflexible about the things the designers think are least likely to change.That's what refactoring is all about, of course. Sometimes, a particular guess turns out to be wrong. We have the wrong factors, the wrong components, for adding a new feature. So we change the shape of the code -- we factor it into different components -- to reflect our new best understanding of the future. Then we move on.
Sometimes, though, there are forces that make more desirable a relatively monolithic piece of code (or, as Braithwaite points out, a system decomposed into relatively less flexible components). In these cases, we need to defactor, to use Braithwaite's term: we recombine some or all of the parts to create a new design.
Predicting the future is hard, even for experienced programmers. One of the goals of agile design is to not think too far ahead, because that means committing to a future too far removed from what we already know to be true about our program.