Summary
How can we see the problems that we face when building software through new eyes?
Advertisement
(This started as a reply to John Camara in the previous blog entry, then took on a life of its own.)
I believe by now it is safe to assume that the majority of software developers and managers of software projects have come to accept the importance of testing so I feel it's unnecessary to comment on testing.
I've learned not to trust the fact that there's a lot of noise about something. It usually doesn't correlate to reality. A friend works for a company that's just now learning about unit testing, and apparently testing in general. I've consulted with companies where testing is still a new thing. My guess is that the majority of software developers have not yet accepted the importance of testing, and that it's only the noisemakers on the leading edge who have been learning and talking about it, and thus giving the impression that it's now well accepted.
Another example of noise vs. reality: people are always saying that there are still more COBOL and FORTRAN programmers out there than any other kind. But when was the last time you saw one, much less talked to one? By that metric, they don't exist. But apparently there are tons of them.
Now as important as it may be to have a second pair of eyes on a problem I feel it's not the most important benefit of code reviews. I feel that code reviews provide a means of mentoring each other.
Yes, it's one thing to show my examples and talk about how you would, in theory, use a particular language feature properly. But when you work with code that someone actually have a vested interest in, then it becomes real and important. (I create toy examples in books out of necessity only).
I think that the more abstract the concept, the more important it is to work with a project that people are actually trying to build, to take it out of the realm of ideas. For example, when teaching OO design (which is more abstract than programming), I encourage people to bring their own designs, so we can work on them together. This not only brings the importance up, but it also makes better use of the training or consulting time, because people can actually gain forward motion on their projects.
This form of mentoring is likely to be the only form of mentoring that the majority of developers experience these days. After all, mentoring has lost most, if not all, priority in these sad times of ever decreasing costs at all costs. We have simply forgotten how important it is to pass collective experiences from generation to generation.
This may come from (perceived) efficiency considerations. Mentoring on a regular basis may appear to be just a cost for a project -- interference with getting things done. Whereas carving out a week for training between projects is a discrete chunk of time, you do it and you're done and then people can get back to slinging code as fast as they can. Or any number of other scenarios.
I think the problem is that while many programmers understand that programming happens in the mind, and the code itself is just an artifact of the process, outside the field, the code looks like it's what you're doing. (An understandable perception, since the code is the core deliverable). So if it's about the code and not the mental process behind the code, it makes sense that you would do whatever you can to produce the code as fast and as cheaply as possible, and to discard anything that appears to hinder the creation of code. From this follows the logical ideas that coding is typing, so you want to see people typing all the time, and that 10 people can type faster than one person so if you can hire ten Indians cheaper than one US programmer, you're going to get a lot more typing done, so you'll get big economic leverage.
In reality, study after study indicates that success comes from who you hire. This suggests that programming is not a mass-production activity where programmers are replaceable components.
I think a good analogy is writing a novel. Suppose you want to create a Stephen King novel (I'm not much of a fan, but this is exactly the kind of book that publishers stay up nights trying to figure out how to mass produce). You could say, "A book is made up of words, and words are created by typing, so to create a book we need to get a bunch of people typing. The more people we can get typing, the faster we'll create a book. And the cheaper the typist, the cheaper it will be to create books."
It's hard to argue with that logic. After all, a book is made up of words. And words are created by typing, etc. But anyone who reads novels knows that there must be a fundamental flaw in the logic, because there are authors whom you like and others whom you can't stand. The choice of words and the structure of the book is what makes the difference, and that is based on the person writing the book. We know you can't replace one author with 10 lesser writers and get anything like what the author could produce, or anything you'd want to read.
Another example is a house. Like software, it's comprised of subsystems that fit together. Like software, you have a bunch of people working on it, and it's even true that some of those people are replaceable. It doesn't really matter who is nailing up the wallboard. But you really notice the design of the house, and you notice how well it was put together, and those things are determined by the architect and the builder.
I've been struggling with this general problem for a long time. That is, the "logical" arguments that are very hard to refute, like "software is created by typing." True on the surface, but not really the essence of the issue. But if you keep the argument on those terms, you can't really get anywhere, because the logic is irrefutable. Even if that logic completely misses the real issue.
This is probably why I keep fighting with the static-dynamic language debate, because it has the same feel to me. You can come up with all kinds of reasons that static checking is a good thing, until you have an experience programming in a dynamic language where you are vastly more productive than with a static language. But that experience defies the logic used to back up the reasoning behind static languages.
Here's another one. I believe that "details matter," and that noise really does wear you down (studies show that noise makes you tired). What I'm talking about here is visual and complexity noise. So I was disappointed when, for example, Ruby turned out to have begin and end statements, and that it uses "new" to create objects. These are all noise artifacts from previous languages, required to support their compilers. If your language creates all objects on the heap, you don't need to say "new" to distinguish between heap and stack objects (like you do in C++, which was mindlessly mimicked by Java). And everyone always indents their code, so you can use indentation to establish scope. Besides the fact that I'm justifying the design minimalism of Python here, when I put these ideas out I will probably get a lot of perfectly reasonable rationalizations about why this is the best way of doing things. And without questioning the fundamental principles upon which those arguments are founded, those arguments will be pretty airtight, even if they really come down to "I'm used to that and I don't want to think differently about it."
Java has always required a lot of extra typing. But the fact that Eclipse and other IDEs generate code for you seems to justify enormous amounts of visual noise, and for those in the midst of it, that's OK, and even desirable. "It's clearer because it's more explicit" (Python even has a maxim "Explicit is better than implicit"). This is even taken to extremes with the idea, supported by a surprising number of folks, that every class should have an associated interface, which to my mind makes the code far more complicated and confusing. Which IMO costs money, because everyone who works with that code must wade through all those extra layers of complication.
All of this detail costs time and money, even if you have a tool generating a lot of code for you. But if you're in the middle of it, it's all you can see and it makes sense because it seems to work. And of course, if you compare one Java project to another, you aren't questioning the cost of using the language.
In contrast, when I teach OO design, my favorite approach is to (A) work on a project that the client is actually working on and (B) move quickly through the design process and model the result in a dynamic language (Python is what I know best). In most cases, the client doesn't know Python, but that doesn't matter. We still very quickly get a model of the system up and running, and in the process we discover problems that our initial design pass didn't see. So because of the speed and agility of a dynamic language, design issues appear early and quickly and we can refine the design before recasting it in a heavyweight language.
And I would argue that if the initial code is done in the heavyweight language instead, then (A) There is resistance to putting the design into code because it is much more work intensive; it isn't a lightweight activity, and (B) There is resistance to making changes to the design for the same reason.
And yet, I will probably get any number of perfectly reasonable arguments to the effect that this approach doesn't make sense. I usually find that these arguments are not based on experience, but on logic that follows from fundamental assumptions about the world of programming.
It may not even be possible to prove things logically when it comes to programming. So many of the conclusions that we draw this way appear to be wrong. This is what I like about the book "Peopleware," and also "Software Conflict 2.0" that I'm now reading. These books point out places where we operate based on what seems perfectly logical, and yet is wrong (one of my favorite studies in "Peopleware" shows that, of all forms of estimation, the most productive approach is when no estimate at all is made).
The story that I heard about the Greek Natural Philosophers (what we call physicists today) is that they were more interested in the arguments about how something worked than they were about how that thing actually worked. So they didn't drop small and large stones in order to find out if one fell faster than they other, they argued based on their assumptions.
It seems to me that we're in the same situation when we try to argue about programming. A large part of the Enlightenment came from the move to the scientific method, which seems like a small, simple step but turned out to be very big, and to have a very big impact. To wit, you can argue about how you think something will happen, but then you have to go out and actually do the experiment. If the experiment disagrees with your argument, then you have to change your argument and try another experiment.
The key is in doing the experiment, and in paying attention to the results, rather than starting with belief and trying to wrestle the world into line with that belief. Even after some 500 years, human society is still trying to come to terms with the age of reason.
I think the essence of what the agilists are doing is a perfect analogy to the discovery of the scientific method. Instead of making stuff up -- and if you look back at all the "solutions" we've invented to solve software complexity problems, that's primarily what they are -- you do an experiment and see what happens. And if the experiment denies the arguments you've used in the past, you can't discard the results of the experiment. You have to change something about your argument.
Of course, you aren't forced to change your argument. But even if it doesn't happen overnight, those that look at the experiments and realize that something is different than the way they thought it was, those people will move past you and forge into new territory. Territory that your company may not be able to enter if they refuse to change their ideas.
> Another example of noise vs. reality: people are always > saying that there are still more COBOL and FORTRAN > programmers out there than any other kind. But when was > the last time you saw one, much less talked to one? By > that metric, they don't exist. But apparently there are > tons of them.
Actually, where I work ia lot of COBOL developers and only a handful of Java developers.
> In reality, study after study indicates that success > comes from who you hire. This suggests that programming is > not a mass-production activity where programmers are > replaceable components.
Exactly! I don't know how many times I've bugged my cube-neighbor saying pretty much this same thing.
"And everyone always indents their code, so you can use indentation to establish scope."
Without getting into a religious debate, I'll disagree.
While I think the indentation rule can be *widely* applied, I don't agree with the "always" part when it comes to binding formatting to function. Obviously, if you use a language that does so, e.g. Python, it seems natural after spending some time with the language. So, in the case of Python, I think it works. It is a part of Python, giving it a great deal of its "flavor". I think it could also work for other languages that have a rigid syntax; but I wouldn't want all languages to follow that rule.
There is a lot of talk about refactoring these days, and there have been automatic code formatters for a long time; these tools work against rules that are relatively easy to apply, given a corpus of code, but they aren't all that smart. They have to be completely objective about what they do. They don't (at least not yet) lay out the code, look at it, hem and hah over it, and try to decide what layout or structure makes the code easy to read and understand.
I believe in the power of consistency, but I don't want to fall back on it as a crutch if I can do better.
Ultimately, the criteria is "does <feature-x> help us build better software, and do so more effectively". Indentation based scoping, like every other language feature I can think of, falls under the "sometimes" area. I'm not saying it's bad, I just don't agree with the "always" part.
'Java has always required a lot of extra typing. "It's clearer because it's more explicit" (Python even has a maxim "Explicit is better than implicit"). This is even taken to extremes with the idea, supported by a surprising number of folks, that every class should have an associated interface, which to my mind makes the code far more complicated and confusing. Which IMO costs money, because everyone who works with that code must wade through all those extra layers of complication.'
I whole-heartedly subscribe to the "Explicit is better than implicit" paradigm. You really need to distinguish between noisy redundancy and helpful explicitness. Communication in natural language wouldn't even be possible without a certain measure of redundancy. Likewise, program code without redundancy is unintelligible. Only the machine can do without redundancy, no human programmer can - that's why we don't write machine code. Your complaint about too much typing and redundancy in certain languages is irrelevant as long as you can't give criteria for what you consider as "noise". You should also acknowledge that the perception of what amount of redundancy is appropriate is not the same whether you write your own code or whether you maintain somebody else's code.
I have also come across the idea to duplicate almost every class with an interface. This has nothing to do with explicitness. I suspect it's a misundertanding of OO design priciples. In theory, you can reimplement your interfaces to plug into a different datasource or whatever, but how likely is this to happen? This happens when principles are applied without real understanding. A lot of noise results from people not understanding what they are doing.
I really liked the book analogy. I'll remember that the next time I need to explain things to a non-programmer.
I was a little unclear about the following line about Ruby, though:
it uses "new" to create objects. These are all noise artifacts from previous languages, required to support their compilers. If your language creates all objects on the heap, you don't need to say "new" to distinguish between heap and stack objects"
You're not arguing against creating instances by calling a method on a class, right? You're simply requesting some Python style syntactic sugar to make "SomeClass(params)" equivalent to "SomeClass.new(params)" just like Python's "SomeClass(params)" is equivalent to "SomeClass.__call__()"?
Ruby does occasionally provide sugar for call operations using the indexing operator []. For example, instead of writing "proc.call(params)", you can write "proc[params]". In order to make instance creation use this syntax you could run:
class Object def Object.[](*params, &block) self.new(*params, &block) end end
This lets you create objects using "Object[]" or "SomeObject[param1, param2]". Of course, this isn't really a general solution, since some classes already have [] class methods (often for variant constructors, actually). And it doesn't look as good as ().
Unfortunately, it seems like adding the ability to sugar () to a method call would require pretty major changes to Ruby's semantics and maybe its method call syntax. I actually still haven't made up my mind whether I like Ruby or Python's method calling semantics better (Ruby: 1 step, message send. Python: 2 step, retrieve a method object and then trigger).
> I think the essence of what the agilists are doing is a > perfect analogy to the discovery of the scientific method. > Instead of making stuff up -- and if you look back at all > the "solutions" we've invented to solve software > complexity problems, that's primarily what they are -- you > do an experiment and see what happens. And if the > experiment denies the arguments you've used in the past, > you can't discard the results of the experiment. You have > to change something about your argument.
After reading G. Chaitin's book "Meta Math - The Quest for Omega" (summarized in SciAmer http://www.cs.auckland.ac.nz/CDMTCS/chaitin/sciamer3.html ) I realized that in order to completely verify a software system it is required to employ resources which have at least the same complexity as the system itself. Complexity means effort(and that means $$$$), therefore an organization should spend the same effort for verification as it spends for development.
Specification and design are not in the same ballpark because, by their nature, they require a certain level of incompleteness and ambiguity. After all, the specs and the design are just a blueprint for the program and not the program itself.
At the end of the day though, we have a problem at hand and a solution for it in the running code. The complexity of the problem is directly reflected in the complexity of the solution. Further on, getting the proof we have a valid solution is as complex as the problem itself.
Agile dev processes do manage complexity, incompleteness and ambiguity in mainly two ways. They do not allow us to introduce too much complexity in the system at any point in time. And for any change we make, a proof of a working system is required.
I think there is a basic misunderstanding of what programming is by non-programmers. The desire to treat code-writing as a commodity or fungible task is pervasive (and understandable). Programming is not bridge building.
If you were building a bridge it would likely be similar to a bridge already in existance. At most you may have one new idea or concept. The main job is still building a bridge that is mostly similar to what is already made and hence a relatively well defined problem.
The difference in programming is that the one new concept is the task. You don't (or shouldn't be) programming things that already exist. Because code isn't physical you can copy, reuse, already extant parts. That part is free (unlike a bridge building project).
In every you should be doing something that has never been done before. That is, you are tackling a problem you know very little about. This is why waterfall development is so perilous. You get a bunch of people together who know very little about what they're planning to do. No wonder things go pear-shaped more often than not. It's not a engineering problem, at least in the traditional sense.
Because of the nature of programming, you can't have a bunch of "typists" doing it. It's a craft and requires a craftsman to do it. Actually *writing* code is easy, it's the figuring out how to write it that's hard.
I had a junior programmer say to me that he was frustrated that he still didn't grasp the general rules on how to solve common programming problems. I guess he was having to think too much. I told him that there are no such rules - the job is to make them yourself. Any problem easily defined and solved can just as easily be done by another program. A hard problem like Object to Relational mapping is addressed by Hibernate. My job is to do the next thing that hasn't been figured out yet.
It's a brutal cycle where you can never rest on your laurels. You're always inventing. I think it's very stimulating, but it's certainly not for everyone.
The venerable "Mythical Man Month" estimated that the best programmers are 10 times more productive than the worst. It's funny how many lessons were figured out so long ago and are still not accepted.
I think the essence of what the agilists are doing is a perfect analogy to the discovery of the scientific method. Instead of making stuff up -- and if you look back at all the "solutions" we've invented to solve software complexity problems, that's primarily what they are -- you do an experiment and see what happens. And if the experiment denies the arguments you've used in the past, you can't discard the results of the experiment. You have to change something about your argument.
-snip- The story that I heard about the Greek Natural Philosophers (what we call physicists today) is that they were more interested in the arguments about how something worked than they were about how that thing actually worked. So they didn't drop small and large stones in order to find out if one fell faster than they other, they argued based on their assumptions.
Synchronicity? "Back to a kids' science project: studying gravity, and repeating Galileo's experiment. Kids (this time about 10-11 years old I believe) climb a garage and attempt to measure with a stopwatch how long various objects take to fall. One girl comes up with the big idea: drop two objects simultaneously, which makes comparison much simpler. Kids are scientists!" Guido van Rossum http://www.artima.com/weblogs/viewpost.jsp?thread=167318
A large part of the Enlightenment came from the move to the scientific method... The key is in doing the experiment, and in paying attention to the results, rather than starting with belief and trying to wrestle the world into line with that belief. Even after some 500 years, human society is still trying to come to terms with the age of reason. And then there was Newton. http://www.nypl.org/research/newton/introduction.html
Christopher Hitchens distilled some of this into a simple phrase what can be asserted without evidence can also be dismissed without evidence.
From this follows the logical ideas that coding is typing, so you want to see people typing all the time, and that 10 people can type faster than one person so if you can hire ten Indians cheaper than one US programmer, you're going to get a lot more typing done, so you'll get big economic leverage.
In reality, study after study indicates that success comes from who you hire. This suggests that programming is not a mass-production activity where programmers are replaceable components.
I've worked with Indian programmers who were fabulous, and US programmers who were appallingly bad.
Of course, you aren't forced to change your argument "When the facts change, I change my mind. What do you do, sir?" John Meynard Keynes
"We found that, for outsiders, the key is to understand that the Toyota Production System creates a community of scientists. Whenever Toyota defines a specification, it is establishing sets of hypotheses that can then be tested. In other words, it is following the scientific method. To make any changes, Toyota uses a rigorous problem-solving process that requires a detailed assessment of the current state of affairs and a plan for improvement that is, in effect, an experimental test of the proposed changes. With anything less that such scientific rigour, change at Toyota would amount to little more than random trial and error - a blindfolded walk through life."
Spear, S. and Brown, H.K. (September-October 1999). "Decoding the DNA of the Toyota Production System." Harvard Business Review, 97-106.
Spear, Steven J. (May 2004). "Learning to Lead at Toyota." Harvard Business Review, 78-86.
Whilst modern IDEs do reduce typing; some people prefer a text editor, some environments like embedded can't run IDEs, and there is always the need to present textual examples in papers, tutorials, books, etc. Therefore I have proposed some minor syntax changes to Java that substantially reduce verbosity, yet are backwards compatible. You can vote for shorther syntax at:
ArrayList< String > strings = new ArrayList< String >();
Allow:
var strings = new ArrayList< String >(); // also see 1 below
Similarly allow:
final strings = new ArrayList< String >();
1. Make the new keyword optional.
EG instead of:
String s = new String();
Allow:
String s = String();
Combining 0 and 1 allow:
var strings = ArrayList< String >();
final strings = ArrayList< String >();
2. For methods and classes make the curly braces optional if there is only one statement. IE like for, if, etc.
EG instead of:
class IntWrapper {
publicint i;
}
Allow:
class IntWrapper publicint i;
And instead of:
public String toString() {
return "Hello";
}
Allow:
public String toString() return "Hello";
3. If a class needs to only implement a single method that is defined in either an abstract class of an interface, then allow the method qualifiers, return type, name, and the arguments types to be omitted.
> > I believe by now it is safe to assume that the majority of > software developers and managers of software projects have > come to accept the importance of testing so I feel it's > unnecessary to comment on testing. > I believe you are wrong. I think the mistake comes from assuming that the programmers who go to conferences and inhabit forums represent the majority that you refer to.
I have just joined a project that has been running for almost five years and is due to deliver this summer. Having looked through the code, I haven't found any unit tests at all. This is hardly surprising though since the actual code is written in an untestable style (i.e. procedural code (100+ line methods) wrapped up in arbitrary objects). The project is now supposidly undergoing user acceptance testing (i.e. it's a predominantly waterfall style project) however the testing users' attitudes are very much along the lines that since they're not programmers they can hardly be expected to know how a computer program should work.
I think that this still represents the majority of software being written on the 'outside world' (by which I mean stuff written by hack programmers who've done their Java training course and are delivering software for use by non-computer specialists). You'll rarely hear about it though, since these programmers don't go to conferences, don't inhabit forums and don't read books. Nevertheless, they are still the majority, therefore it is always necessary to comment on testing.
Flat View: This topic has 57 replies
on 4 pages
[
1234
|
»
]